diff --git a/README.md b/README.md index 3aa06ea..e5bfa51 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,11 @@ Socket.IO-client for iOS/OS X. ##Example ```swift +<<<<<<< HEAD let socket = SocketIOClient(socketURL: NSURL(string: "http://localhost:8080")!, options: [.log(true), .forcePolling(true)]) +======= +let socket = SocketIOClient(socketURL: NSURL(string: "http://localhost:8080")!, config: [.Log(true), .ForcePolling(true)]) +>>>>>>> development socket.on("connect") {data, ack in print("socket connected") @@ -27,7 +31,7 @@ socket.connect() ##Objective-C Example ```objective-c NSURL* url = [[NSURL alloc] initWithString:@"http://localhost:8080"]; -SocketIOClient* socket = [[SocketIOClient alloc] initWithSocketURL:url options:@{@"log": @YES, @"forcePolling": @YES}]; +SocketIOClient* socket = [[SocketIOClient alloc] initWithSocketURL:url config:@{@"log": @YES, @"forcePolling": @YES}]; [socket on:@"connect" callback:^(NSArray* data, SocketAckEmitter* ack) { NSLog(@"socket connected"); @@ -141,7 +145,7 @@ Run `seed install`. ##API Constructors ----------- -`init(var socketURL: NSURL, options: Set = [])` - Creates a new SocketIOClient. options is a Set of SocketIOClientOption. If your socket.io server is secure, you need to specify `https` in your socketURL. +`init(var socketURL: NSURL, config: SocketIOClientConfiguration = [])` - Creates a new SocketIOClient. options is a Set of SocketIOClientOption. If your socket.io server is secure, you need to specify `https` in your socketURL. `convenience init(socketURL: NSURL, options: NSDictionary?)` - Same as above, but meant for Objective-C. See Options on how convert between SocketIOClientOptions and dictionary keys. diff --git a/Socket.IO-Client-Swift.xcodeproj/project.pbxproj b/Socket.IO-Client-Swift.xcodeproj/project.pbxproj index 0a092d7..9821a8c 100644 --- a/Socket.IO-Client-Swift.xcodeproj/project.pbxproj +++ b/Socket.IO-Client-Swift.xcodeproj/project.pbxproj @@ -115,6 +115,10 @@ 7472C65D1BCAB53E003CA70D /* SocketNamespacePacketTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7472C65B1BCAB53E003CA70D /* SocketNamespacePacketTest.swift */; }; 7472C65F1BCAC46E003CA70D /* SocketSideEffectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7472C65E1BCAC46E003CA70D /* SocketSideEffectTest.swift */; }; 7472C6601BCAC46E003CA70D /* SocketSideEffectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7472C65E1BCAC46E003CA70D /* SocketSideEffectTest.swift */; }; + 747BC5991D5F943500CA5FA4 /* SocketIOClientConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 747BC5981D5F943500CA5FA4 /* SocketIOClientConfiguration.swift */; }; + 747BC59A1D5F943500CA5FA4 /* SocketIOClientConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 747BC5981D5F943500CA5FA4 /* SocketIOClientConfiguration.swift */; }; + 747BC59B1D5F943500CA5FA4 /* SocketIOClientConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 747BC5981D5F943500CA5FA4 /* SocketIOClientConfiguration.swift */; }; + 747BC59F1D5F9BA200CA5FA4 /* SocketIOClientConfigurationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 747BC59E1D5F9BA200CA5FA4 /* SocketIOClientConfigurationTest.swift */; }; 749642B51D3FCE5500DD32D1 /* SSLSecurity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749642B31D3FCE5500DD32D1 /* SSLSecurity.swift */; }; 749642B61D3FCE5500DD32D1 /* SSLSecurity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749642B31D3FCE5500DD32D1 /* SSLSecurity.swift */; }; 749642B71D3FCE5500DD32D1 /* SSLSecurity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749642B31D3FCE5500DD32D1 /* SSLSecurity.swift */; }; @@ -192,6 +196,8 @@ 74321DCA1C2D939A00CF6F43 /* SocketParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketParserTest.swift; sourceTree = ""; }; 7472C65B1BCAB53E003CA70D /* SocketNamespacePacketTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketNamespacePacketTest.swift; sourceTree = ""; }; 7472C65E1BCAC46E003CA70D /* SocketSideEffectTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketSideEffectTest.swift; sourceTree = ""; }; + 747BC5981D5F943500CA5FA4 /* SocketIOClientConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketIOClientConfiguration.swift; path = Source/SocketIOClientConfiguration.swift; sourceTree = ""; }; + 747BC59E1D5F9BA200CA5FA4 /* SocketIOClientConfigurationTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketIOClientConfigurationTest.swift; sourceTree = ""; }; 749642B31D3FCE5500DD32D1 /* SSLSecurity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SSLSecurity.swift; path = Source/SSLSecurity.swift; sourceTree = ""; }; 749642B41D3FCE5500DD32D1 /* WebSocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WebSocket.swift; path = Source/WebSocket.swift; sourceTree = ""; }; 74ABF7761C3991C10078C657 /* SocketIOClientSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketIOClientSpec.swift; path = Source/SocketIOClientSpec.swift; sourceTree = ""; }; @@ -314,6 +320,7 @@ 74F124EF1BC574CF002966F4 /* SocketBasicPacketTest.swift */, 741F39ED1BD025D80026C9CC /* SocketEngineTest.swift */, 7472C65B1BCAB53E003CA70D /* SocketNamespacePacketTest.swift */, + 747BC59E1D5F9BA200CA5FA4 /* SocketIOClientConfigurationTest.swift */, 742D150B1CA5794B00BD987D /* SocketObjectiveCTest.m */, 74321DCA1C2D939A00CF6F43 /* SocketParserTest.swift */, 7472C65E1BCAC46E003CA70D /* SocketSideEffectTest.swift */, @@ -346,6 +353,7 @@ 74171E571C10CD240062D398 /* SocketEventHandler.swift */, CEBA56991CDA0B8200BA0389 /* SocketExtensions.swift */, 74171E591C10CD240062D398 /* SocketIOClient.swift */, + 747BC5981D5F943500CA5FA4 /* SocketIOClientConfiguration.swift */, 74171E5A1C10CD240062D398 /* SocketIOClientOption.swift */, 74ABF7761C3991C10078C657 /* SocketIOClientSpec.swift */, 74171E5B1C10CD240062D398 /* SocketIOClientStatus.swift */, @@ -624,6 +632,7 @@ 74171EB71C10CD240062D398 /* SocketParsable.swift in Sources */, 74171E811C10CD240062D398 /* SocketEnginePacketType.swift in Sources */, 74171E6F1C10CD240062D398 /* SocketAnyEvent.swift in Sources */, + 747BC5991D5F943500CA5FA4 /* SocketIOClientConfiguration.swift in Sources */, 74171E9F1C10CD240062D398 /* SocketIOClientOption.swift in Sources */, 74BC45AB1D0C6675008CC431 /* SocketClientManager.swift in Sources */, ); @@ -681,6 +690,7 @@ 74171EB91C10CD240062D398 /* SocketParsable.swift in Sources */, 74171E831C10CD240062D398 /* SocketEnginePacketType.swift in Sources */, 74171E711C10CD240062D398 /* SocketAnyEvent.swift in Sources */, + 747BC59A1D5F943500CA5FA4 /* SocketIOClientConfiguration.swift in Sources */, 74171EA11C10CD240062D398 /* SocketIOClientOption.swift in Sources */, 74BC45AC1D0C6675008CC431 /* SocketClientManager.swift in Sources */, ); @@ -693,6 +703,7 @@ 742D150C1CA5794B00BD987D /* SocketObjectiveCTest.m in Sources */, 74321DCB1C2D939A00CF6F43 /* SocketAckManagerTest.swift in Sources */, 74321DCC1C2D939A00CF6F43 /* SocketParserTest.swift in Sources */, + 747BC59F1D5F9BA200CA5FA4 /* SocketIOClientConfigurationTest.swift in Sources */, 7472C6601BCAC46E003CA70D /* SocketSideEffectTest.swift in Sources */, 74171EA81C10CD240062D398 /* SocketIOClientStatus.swift in Sources */, 741F39EF1BD025D80026C9CC /* SocketEngineTest.swift in Sources */, @@ -726,6 +737,7 @@ 74171EBB1C10CD240062D398 /* SocketParsable.swift in Sources */, 74171E851C10CD240062D398 /* SocketEnginePacketType.swift in Sources */, 74171E731C10CD240062D398 /* SocketAnyEvent.swift in Sources */, + 747BC59B1D5F943500CA5FA4 /* SocketIOClientConfiguration.swift in Sources */, 74171EA31C10CD240062D398 /* SocketIOClientOption.swift in Sources */, 74BC45AD1D0C6675008CC431 /* SocketClientManager.swift in Sources */, ); diff --git a/SocketIO-MacTests/SocketIOClientConfigurationTest.swift b/SocketIO-MacTests/SocketIOClientConfigurationTest.swift new file mode 100644 index 0000000..0760438 --- /dev/null +++ b/SocketIO-MacTests/SocketIOClientConfigurationTest.swift @@ -0,0 +1,46 @@ +// +// TestSocketIOClientConfiguration.swift +// Socket.IO-Client-Swift +// +// Created by Erik Little on 8/13/16. +// +// + +import XCTest +import SocketIOClientSwift + +class TestSocketIOClientConfiguration: XCTestCase { + var config = [] as SocketIOClientConfiguration + + override func setUp() { + super.setUp() + + config = [.log(false), .forceNew(true)] + } + + func testReplaceSameOption() { + config.insert(.log(true)) + + XCTAssertEqual(config.count, 2) + + switch config[0] { + case let .log(log): + XCTAssertTrue(log) + default: + XCTFail() + } + } + + func testIgnoreIfExisting() { + config.insert(.forceNew(false), replacing: false) + + XCTAssertEqual(config.count, 2) + + switch config[1] { + case let .forceNew(new): + XCTAssertTrue(new) + default: + XCTFail() + } + } +} diff --git a/SocketIO-MacTests/SocketObjectiveCTest.m b/SocketIO-MacTests/SocketObjectiveCTest.m index 6043a24..c8a4956 100644 --- a/SocketIO-MacTests/SocketObjectiveCTest.m +++ b/SocketIO-MacTests/SocketObjectiveCTest.m @@ -21,7 +21,7 @@ - (void)setUp { [super setUp]; NSURL* url = [[NSURL alloc] initWithString:@"http://localhost"]; - self.socket = [[SocketIOClient alloc] initWithSocketURL:url options:nil]; + self.socket = [[SocketIOClient alloc] initWithSocketURL:url config:nil]; } - (void)testOnSyntax { diff --git a/Source/SocketEngine.swift b/Source/SocketEngine.swift index d687162..5c7ce23 100644 --- a/Source/SocketEngine.swift +++ b/Source/SocketEngine.swift @@ -80,11 +80,11 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll private var selfSigned = false private var voipEnabled = false - public init(client: SocketEngineClient, url: URL, options: Set) { + public init(client: SocketEngineClient, url: URL, config: SocketIOClientConfiguration) { self.client = client self.url = url - for option in options { + for option in config { switch option { case let .connectParams(params): connectParams = params @@ -125,9 +125,9 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll (urlPolling, urlWebSocket) = createURLs() } - + public convenience init(client: SocketEngineClient, url: URL, options: NSDictionary?) { - self.init(client: client, url: url, options: options?.toSocketOptionsSet() ?? []) + self.init(client: client, url: url, config: options?.toSocketConfiguration() ?? []) } deinit { diff --git a/Source/SocketExtensions.swift b/Source/SocketExtensions.swift index 71e14d2..10de6ad 100644 --- a/Source/SocketExtensions.swift +++ b/Source/SocketExtensions.swift @@ -96,12 +96,12 @@ extension NSDictionary { } } - func toSocketOptionsSet() -> Set { - var options = Set() + func toSocketConfiguration() -> SocketIOClientConfiguration { + var options = [] as SocketIOClientConfiguration for (rawKey, value) in self { if let key = rawKey as? String, let opt = NSDictionary.keyValueToSocketIOClientOption(key: key, value: value) { - options.insertIgnore(opt) + options.insert(opt) } } @@ -109,14 +109,6 @@ extension NSDictionary { } } -extension Set where Element : ClientOption { - mutating func insertIgnore(_ element: Element) { - if !contains(element) { - insert(element) - } - } -} - extension String { func toArray() throws -> [AnyObject] { guard let stringData = data(using: .utf8, allowLossyConversion: false) else { return [] } diff --git a/Source/SocketIOClient.swift b/Source/SocketIOClient.swift index 4d9b645..4c918ac 100644 --- a/Source/SocketIOClient.swift +++ b/Source/SocketIOClient.swift @@ -42,12 +42,9 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable public var forceNew = false public var nsp = "/" - public var options: Set + public var config: SocketIOClientConfiguration public var reconnects = true public var reconnectWait = 10 - public var sid: String? { - return nsp + "#" + (engine?.sid ?? "") - } private let ackQueue = DispatchQueue(label: "com.socketio.ackQueue", attributes: []) private let emitQueue = DispatchQueue(label: "com.socketio.emitQueue", attributes: []) @@ -66,16 +63,20 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable var waitingPackets = [SocketPacket]() + public var sid: String? { + return nsp + "#" + (engine?.sid ?? "") + } + /// Type safe way to create a new SocketIOClient. opts can be omitted - public init(socketURL: URL, options: Set = []) { - self.options = options + public init(socketURL: URL, config: SocketIOClientConfiguration = []) { + self.config = config self.socketURL = socketURL - if socketURL.absoluteString.hasPrefix("https://") ?? false { - self.options.insertIgnore(.secure(true)) + if socketURL.absoluteString.hasPrefix("https://") { + self.config.insert(.secure(true)) } - for option in options { + for option in config { switch option { case let .reconnects(reconnects): self.reconnects = reconnects @@ -97,16 +98,16 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable continue } } - - self.options.insertIgnore(.path("/socket.io/")) + + self.config.insert(.path("/socket.io/"), replacing: false) super.init() } /// Not so type safe way to create a SocketIOClient, meant for Objective-C compatiblity. /// If using Swift it's recommended to use `init(socketURL: NSURL, options: Set)` - public convenience init(socketURL: URL, options: NSDictionary?) { - self.init(socketURL: socketURL, options: options?.toSocketOptionsSet() ?? []) + public convenience init(socketURL: NSURL, config: NSDictionary?) { + self.init(socketURL: socketURL as URL, config: config?.toSocketConfiguration() ?? []) } deinit { @@ -117,7 +118,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable private func addEngine() -> SocketEngineSpec { DefaultSocketLogger.Logger.log("Adding engine", type: logType, args: "") - engine = SocketEngine(client: self, url: socketURL, options: options) + engine = SocketEngine(client: self, url: socketURL, config: config) return engine! } diff --git a/Source/SocketIOClientConfiguration.swift b/Source/SocketIOClientConfiguration.swift new file mode 100644 index 0000000..07aba15 --- /dev/null +++ b/Source/SocketIOClientConfiguration.swift @@ -0,0 +1,108 @@ +// +// SocketIOClientConfiguration.swift +// Socket.IO-Client-Swift +// +// Created by Erik Little on 8/13/16. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +public struct SocketIOClientConfiguration : ExpressibleByArrayLiteral, Collection, MutableCollection { + public typealias Element = SocketIOClientOption + public typealias Index = Array.Index + public typealias Generator = Array.Generator + public typealias SubSequence = Array.SubSequence + + private var backingArray = [SocketIOClientOption]() + + public var startIndex: Index { + return backingArray.startIndex + } + + public var endIndex: Index { + return backingArray.endIndex + } + + public var isEmpty: Bool { + return backingArray.isEmpty + } + + public var count: Index.Stride { + return backingArray.count + } + + public var first: Generator.Element? { + return backingArray.first + } + + public subscript(position: Index) -> Generator.Element { + get { + return backingArray[position] + } + + set { + backingArray[position] = newValue + } + } + + public subscript(bounds: Range) -> SubSequence { + get { + return backingArray[bounds] + } + + set { + backingArray[bounds] = newValue + } + } + + public init(arrayLiteral elements: Element...) { + backingArray = elements + } + + public func generate() -> Generator { + return backingArray.makeIterator() + } + + public func index(after i: Index) -> Index { + return backingArray.index(after: i) + } + + public mutating func insert(_ element: Element, replacing replace: Bool = true) { + for i in 0.. SubSequence { + return backingArray.prefix(upTo: end) + } + + public func prefix(through position: Index) -> SubSequence { + return backingArray.prefix(through: position) + } + + public func suffix(from start: Index) -> SubSequence { + return backingArray.suffix(from: start) + } +} diff --git a/Source/SocketIOClientOption.swift b/Source/SocketIOClientOption.swift index 26fb92f..b20c438 100644 --- a/Source/SocketIOClientOption.swift +++ b/Source/SocketIOClientOption.swift @@ -24,7 +24,7 @@ import Foundation -protocol ClientOption : CustomStringConvertible, Hashable { +protocol ClientOption : CustomStringConvertible, Equatable { func getSocketIOOptionValue() -> AnyObject } @@ -99,10 +99,6 @@ public enum SocketIOClientOption : ClientOption { return description } - public var hashValue: Int { - return description.hashValue - } - func getSocketIOOptionValue() -> AnyObject { let value: AnyObject diff --git a/Source/SocketPacket.swift b/Source/SocketPacket.swift index 3fb2e6a..b98c695 100644 --- a/Source/SocketPacket.swift +++ b/Source/SocketPacket.swift @@ -26,6 +26,10 @@ import Foundation struct SocketPacket { + enum PacketType: Int { + case connect, disconnect, event, ack, error, binaryEvent, binaryAck + } + private let placeholders: Int private static let logType = "SocketPacket" @@ -34,9 +38,8 @@ struct SocketPacket { let id: Int let type: PacketType - enum PacketType: Int { - case connect, disconnect, event, ack, error, binaryEvent, binaryAck - } + var binary: [Data] + var data: [AnyObject] var args: [AnyObject] { if type == .event || type == .binaryEvent && data.count != 0 { @@ -46,8 +49,6 @@ struct SocketPacket { } } - var binary: [Data] - var data: [AnyObject] var description: String { return "SocketPacket {type: \(String(type.rawValue)); data: " + "\(String(data)); id: \(id); placeholders: \(placeholders); nsp: \(nsp)}" @@ -110,27 +111,12 @@ struct SocketPacket { private func createPacketString() -> String { let typeString = String(type.rawValue) - let binaryCountString: String - let nspString: String - let idString: String - - if type == .binaryEvent || type == .binaryAck { - binaryCountString = typeString + String(binary.count) + "-" - } else { - binaryCountString = typeString - } - - if nsp != "/" { - nspString = binaryCountString + nsp + "," - } else { - nspString = binaryCountString - } - - if id != -1 { - idString = nspString + String(id) - } else { - idString = nspString - } + // Binary count? + let binaryCountString = typeString + (type == .binaryEvent || type == .binaryAck ? String(binary.count) + "-" : "") + // Namespace? + let nspString = binaryCountString + (nsp != "/" ? nsp + "," : "") + // Ack number? + let idString = nspString + (id != -1 ? String(id) : "") return completeMessage(idString) }