diff --git a/.gitignore b/.gitignore index ff49d0c..2f7122e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .DS_Store .AppleDouble .LSOverride +*.xcodeproj # Icon must end with two \r Icon @@ -41,3 +42,5 @@ DerivedData *.hmap *.ipa *.xcuserstate + +Socket.IO-Test-Server/node_modules/* diff --git a/README.md b/README.md index 657f2e7..57d1861 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ socket.on("connect") {data, ack in socket.on("currentAmount") {data, ack in if let cur = data?[0] as? Double { - socket.emitWithAck("canUpdate", cur)(timeout: 0) {data in + socket.emitWithAck("canUpdate", cur)(timeoutAfter: 0) {data in socket.emit("update", ["amount": cur + 2.50]) } @@ -26,11 +26,11 @@ socket.connect() ```objective-c SocketIOClient* socket = [[SocketIOClient alloc] initWithSocketURL:@"localhost:8080" options:nil]; -[socket on:@"connect" callback:^(NSArray* data, void (^ack)(NSArray*)) { +[socket onObjectiveC:@"connect" callback:^(NSArray* data, void (^ack)(NSArray*)) { NSLog(@"socket connected"); }]; -[socket on:@"currentAmount" callback:^(NSArray* data, void (^ack)(NSArray*)) { +[socket onObjectiveC:@"currentAmount" callback:^(NSArray* data, void (^ack)(NSArray*)) { double cur = [[data objectAtIndex:0] floatValue]; [socket emitWithAck:@"canUpdate" withItems:@[@(cur)]](0, ^(NSArray* data) { @@ -52,15 +52,16 @@ SocketIOClient* socket = [[SocketIOClient alloc] initWithSocketURL:@"localhost:8 - Can be used from Objective-C ##Installation -Requires Swift 1.2/Xcode 6.3 +Requires Swift 2/Xcode 7 +If you need Swift 1.2/Xcode 6.3/4 use v2 (Pre-Swift 2 support is no longer maintained) If you need Swift 1.1/Xcode 6.2 use v1.5.2. (Pre-Swift 1.2 support is no longer maintained) Carthage ----------------- Add this line to your `Cartfile`: ``` -github "socketio/socket.io-client-swift" ~> 2.3.10 # Or latest version +github "socketio/socket.io-client-swift" ~> 2.4.3 # Or latest version ``` Run `carthage update`. @@ -79,7 +80,7 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' use_frameworks! -pod 'Socket.IO-Client-Swift', '~> 2.3.10' # Or latest version +pod 'Socket.IO-Client-Swift', '~> 2.4.3' # Or latest version ``` Install pods: @@ -121,21 +122,23 @@ Options - `sessionDelegate: NSURLSessionDelegate` Sets an NSURLSessionDelegate for the underlying engine. Useful if you need to handle self-signed certs. Default is nil. - `path: String` - If the server uses a custom path. ex: `"/swift"`. Default is `""` - `extraHeaders: [String: String]?` - Adds custom headers to the initial request. Default is nil. +- `handleQueue: dispatch_queue_t` - The dispatch queue that handlers are run on. Default is the main queue. Methods ------- 1. `on(name:String, callback:((data:NSArray?, ack:AckEmitter?) -> Void))` - Adds a handler for an event. Items are passed by an array. `ack` can be used to send an ack when one is requested. See example. -2. `onAny(callback:((event:String, items:AnyObject?)) -> Void)` - Adds a handler for all events. It will be called on any received event. -3. `emit(event:String, _ items:AnyObject...)` - Sends a message. Can send multiple items. -4. `emit(event:String, withItems items:[AnyObject])` - `emit` for Objective-C -5. `emitWithAck(event:String, _ items:AnyObject...) -> (timeout:UInt64, callback:(NSArray?) -> Void) -> Void` - Sends a message that requests an acknowledgement from the server. Returns a function which you can use to add a handler. See example. Note: The message is not sent until you call the returned function. -6. `emitWithAck(event:String, withItems items:[AnyObject]) -> (UInt64, (NSArray?) -> Void) -> Void` - `emitWithAck` for Objective-C. Note: The message is not sent until you call the returned function. -7. `connect()` - Establishes a connection to the server. A "connect" event is fired upon successful connection. -8. `connect(#timeoutAfter:Int, withTimeoutHandler handler:(() -> Void)?)` - Connect to the server. If it isn't connected after timeoutAfter seconds, the handler is called. -9. `close(#fast:Bool)` - Closes the socket. Once a socket is closed it should not be reopened. Pass true to fast if you're closing from a background task. -10. `reconnect()` - Causes the client to reconnect to the server. -11. `joinNamespace()` - Causes the client to join nsp. Shouldn't need to be called unless you change nsp manually. -12. `leaveNamespace()` - Causes the client to leave the nsp and go back to / +2. `onObjectiveC(name:String, callback:((data:NSArray?, ack:AckEmitter?) -> Void))` - Adds a handler for an event. Items are passed by an array. `ack` can be used to send an ack when one is requested. See example. +3. `onAny(callback:((event:String, items:AnyObject?)) -> Void)` - Adds a handler for all events. It will be called on any received event. +4. `emit(event:String, _ items:AnyObject...)` - Sends a message. Can send multiple items. +5. `emit(event:String, withItems items:[AnyObject])` - `emit` for Objective-C +6. `emitWithAck(event:String, _ items:AnyObject...) -> (timeoutAfter:UInt64, callback:(NSArray?) -> Void) -> Void` - Sends a message that requests an acknowledgement from the server. Returns a function which you can use to add a handler. See example. Note: The message is not sent until you call the returned function. +7. `emitWithAck(event:String, withItems items:[AnyObject]) -> (UInt64, (NSArray?) -> Void) -> Void` - `emitWithAck` for Objective-C. Note: The message is not sent until you call the returned function. +8. `connect()` - Establishes a connection to the server. A "connect" event is fired upon successful connection. +9. `connect(#timeoutAfter:Int, withTimeoutHandler handler:(() -> Void)?)` - Connect to the server. If it isn't connected after timeoutAfter seconds, the handler is called. +10. `close(#fast:Bool)` - Closes the socket. Once a socket is closed it should not be reopened. Pass true to fast if you're closing from a background task. +11. `reconnect()` - Causes the client to reconnect to the server. +12. `joinNamespace()` - Causes the client to join nsp. Shouldn't need to be called unless you change nsp manually. +13. `leaveNamespace()` - Causes the client to leave the nsp and go back to / Client Events ------ diff --git a/Socket.IO-Client-Swift.podspec b/Socket.IO-Client-Swift.podspec index fcfc4fb..ed772a2 100644 --- a/Socket.IO-Client-Swift.podspec +++ b/Socket.IO-Client-Swift.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Socket.IO-Client-Swift" - s.version = "2.3.10" + s.version = "2.4.3" s.summary = "Socket.IO-client for iOS and OS X" s.description = <<-DESC Socket.IO-client for iOS and OS X. @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.author = { "Erik" => "nuclear.ace@gmail.com" } s.ios.deployment_target = '8.0' s.osx.deployment_target = '10.10' - s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v2.3.10' } + s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v2.4.3' } s.source_files = "SocketIOClientSwift/**/*.swift" s.requires_arc = true # s.dependency 'Starscream', '~> 0.9' # currently this repo includes Starscream swift files diff --git a/Socket.IO-Client-Swift.xcodeproj/project.pbxproj b/Socket.IO-Client-Swift.xcodeproj/project.pbxproj index a54247f..73cd272 100644 --- a/Socket.IO-Client-Swift.xcodeproj/project.pbxproj +++ b/Socket.IO-Client-Swift.xcodeproj/project.pbxproj @@ -9,7 +9,6 @@ /* Begin PBXBuildFile section */ 572EF21F1B51F16C00EEBB58 /* SocketIO-iOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 572EF21E1B51F16C00EEBB58 /* SocketIO-iOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; 572EF2251B51F16C00EEBB58 /* SocketIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 572EF2191B51F16C00EEBB58 /* SocketIO.framework */; }; - 572EF22C1B51F16C00EEBB58 /* SocketIO_iOSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 572EF22B1B51F16C00EEBB58 /* SocketIO_iOSTests.swift */; }; 572EF23D1B51F18A00EEBB58 /* SocketIO-Mac.h in Headers */ = {isa = PBXBuildFile; fileRef = 572EF23C1B51F18A00EEBB58 /* SocketIO-Mac.h */; settings = {ATTRIBUTES = (Public, ); }; }; 572EF2431B51F18A00EEBB58 /* SocketIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 572EF2381B51F18A00EEBB58 /* SocketIO.framework */; }; 572EF24A1B51F18A00EEBB58 /* SocketIO_MacTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 572EF2491B51F18A00EEBB58 /* SocketIO_MacTests.swift */; }; @@ -39,6 +38,30 @@ 5764DFA01B51F254004FF46E /* SwiftRegex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5764DF871B51F254004FF46E /* SwiftRegex.swift */; }; 5764DFA11B51F254004FF46E /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5764DF881B51F254004FF46E /* WebSocket.swift */; }; 5764DFA21B51F254004FF46E /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5764DF881B51F254004FF46E /* WebSocket.swift */; }; + 74781D5A1B7E83930042CACA /* SocketIOClientStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74781D591B7E83930042CACA /* SocketIOClientStatus.swift */; }; + 74781D5B1B7E83930042CACA /* SocketIOClientStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74781D591B7E83930042CACA /* SocketIOClientStatus.swift */; }; + 74781D5C1B7E83930042CACA /* SocketIOClientStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74781D591B7E83930042CACA /* SocketIOClientStatus.swift */; }; + 74781D5D1B7E83930042CACA /* SocketIOClientStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74781D591B7E83930042CACA /* SocketIOClientStatus.swift */; }; + 941A4ABA1B67A56C00C42318 /* TestKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = 941A4AB91B67A56C00C42318 /* TestKind.swift */; }; + 94242BB81B67B0E500AAAC9D /* SocketNamespaceAcknowledgementTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94242BB71B67B0E500AAAC9D /* SocketNamespaceAcknowledgementTest.swift */; }; + 945B65351B5FCEEA0081E995 /* SocketAckManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5764DF7C1B51F254004FF46E /* SocketAckManager.swift */; }; + 945B65361B5FCEEA0081E995 /* SocketAnyEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5764DF7D1B51F254004FF46E /* SocketAnyEvent.swift */; }; + 945B65371B5FCEEA0081E995 /* SocketEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5764DF7E1B51F254004FF46E /* SocketEngine.swift */; }; + 945B65381B5FCEEA0081E995 /* SocketEngineClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5764DF7F1B51F254004FF46E /* SocketEngineClient.swift */; }; + 945B65391B5FCEEA0081E995 /* SocketEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5764DF801B51F254004FF46E /* SocketEventHandler.swift */; }; + 945B653A1B5FCEEA0081E995 /* SocketFixUTF8.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5764DF811B51F254004FF46E /* SocketFixUTF8.swift */; }; + 945B653B1B5FCEEA0081E995 /* SocketIOClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5764DF821B51F254004FF46E /* SocketIOClient.swift */; }; + 945B653C1B5FCEEA0081E995 /* SocketLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5764DF831B51F254004FF46E /* SocketLogger.swift */; }; + 945B653D1B5FCEEA0081E995 /* SocketPacket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5764DF841B51F254004FF46E /* SocketPacket.swift */; }; + 945B653E1B5FCEEA0081E995 /* SocketParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5764DF851B51F254004FF46E /* SocketParser.swift */; }; + 945B653F1B5FCEEA0081E995 /* SocketTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5764DF861B51F254004FF46E /* SocketTypes.swift */; }; + 945B65401B5FCEEA0081E995 /* SwiftRegex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5764DF871B51F254004FF46E /* SwiftRegex.swift */; }; + 945B65411B5FCEEA0081E995 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5764DF881B51F254004FF46E /* WebSocket.swift */; }; + 945B65431B63D9DB0081E995 /* SocketEmitTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 945B65421B63D9DB0081E995 /* SocketEmitTest.swift */; }; + 94ADAC491B652D3300FD79AE /* SocketNamespaceEmitTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94ADAC481B652D3300FD79AE /* SocketNamespaceEmitTest.swift */; }; + 94ADAC4B1B6632DD00FD79AE /* SocketAcknowledgementTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94ADAC4A1B6632DD00FD79AE /* SocketAcknowledgementTest.swift */; }; + 94CB8F0B1B6E48B90019ED53 /* SocketTestCases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94CB8F0A1B6E48B90019ED53 /* SocketTestCases.swift */; }; + 94CB8F0D1B6E66E60019ED53 /* AbstractSocketTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94CB8F0C1B6E66E60019ED53 /* AbstractSocketTest.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -64,7 +87,6 @@ 572EF21E1B51F16C00EEBB58 /* SocketIO-iOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SocketIO-iOS.h"; sourceTree = ""; }; 572EF2241B51F16C00EEBB58 /* SocketIO-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SocketIO-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 572EF22A1B51F16C00EEBB58 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 572EF22B1B51F16C00EEBB58 /* SocketIO_iOSTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketIO_iOSTests.swift; sourceTree = ""; }; 572EF2381B51F18A00EEBB58 /* SocketIO.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SocketIO.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 572EF23B1B51F18A00EEBB58 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 572EF23C1B51F18A00EEBB58 /* SocketIO-Mac.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SocketIO-Mac.h"; sourceTree = ""; }; @@ -84,6 +106,14 @@ 5764DF861B51F254004FF46E /* SocketTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketTypes.swift; path = SocketIOClientSwift/SocketTypes.swift; sourceTree = ""; }; 5764DF871B51F254004FF46E /* SwiftRegex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftRegex.swift; path = SocketIOClientSwift/SwiftRegex.swift; sourceTree = ""; }; 5764DF881B51F254004FF46E /* WebSocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WebSocket.swift; path = SocketIOClientSwift/WebSocket.swift; sourceTree = ""; }; + 74781D591B7E83930042CACA /* SocketIOClientStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketIOClientStatus.swift; path = SocketIOClientSwift/SocketIOClientStatus.swift; sourceTree = ""; }; + 941A4AB91B67A56C00C42318 /* TestKind.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestKind.swift; sourceTree = ""; }; + 94242BB71B67B0E500AAAC9D /* SocketNamespaceAcknowledgementTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketNamespaceAcknowledgementTest.swift; sourceTree = ""; }; + 945B65421B63D9DB0081E995 /* SocketEmitTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEmitTest.swift; sourceTree = ""; }; + 94ADAC481B652D3300FD79AE /* SocketNamespaceEmitTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketNamespaceEmitTest.swift; sourceTree = ""; }; + 94ADAC4A1B6632DD00FD79AE /* SocketAcknowledgementTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketAcknowledgementTest.swift; sourceTree = ""; }; + 94CB8F0A1B6E48B90019ED53 /* SocketTestCases.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketTestCases.swift; sourceTree = ""; }; + 94CB8F0C1B6E66E60019ED53 /* AbstractSocketTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AbstractSocketTest.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -163,7 +193,13 @@ 572EF2281B51F16C00EEBB58 /* SocketIO-iOSTests */ = { isa = PBXGroup; children = ( - 572EF22B1B51F16C00EEBB58 /* SocketIO_iOSTests.swift */, + 945B65421B63D9DB0081E995 /* SocketEmitTest.swift */, + 94ADAC4A1B6632DD00FD79AE /* SocketAcknowledgementTest.swift */, + 94ADAC481B652D3300FD79AE /* SocketNamespaceEmitTest.swift */, + 94242BB71B67B0E500AAAC9D /* SocketNamespaceAcknowledgementTest.swift */, + 941A4AB91B67A56C00C42318 /* TestKind.swift */, + 94CB8F0A1B6E48B90019ED53 /* SocketTestCases.swift */, + 94CB8F0C1B6E66E60019ED53 /* AbstractSocketTest.swift */, 572EF2291B51F16C00EEBB58 /* Supporting Files */, ); path = "SocketIO-iOSTests"; @@ -221,6 +257,7 @@ 5764DF801B51F254004FF46E /* SocketEventHandler.swift */, 5764DF811B51F254004FF46E /* SocketFixUTF8.swift */, 5764DF821B51F254004FF46E /* SocketIOClient.swift */, + 74781D591B7E83930042CACA /* SocketIOClientStatus.swift */, 5764DF831B51F254004FF46E /* SocketLogger.swift */, 5764DF841B51F254004FF46E /* SocketPacket.swift */, 5764DF851B51F254004FF46E /* SocketParser.swift */, @@ -413,6 +450,7 @@ 5764DF951B51F254004FF46E /* SocketIOClient.swift in Sources */, 5764DF8B1B51F254004FF46E /* SocketAnyEvent.swift in Sources */, 5764DF971B51F254004FF46E /* SocketLogger.swift in Sources */, + 74781D5A1B7E83930042CACA /* SocketIOClientStatus.swift in Sources */, 5764DFA11B51F254004FF46E /* WebSocket.swift in Sources */, 5764DF991B51F254004FF46E /* SocketPacket.swift in Sources */, 5764DF891B51F254004FF46E /* SocketAckManager.swift in Sources */, @@ -424,7 +462,27 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 572EF22C1B51F16C00EEBB58 /* SocketIO_iOSTests.swift in Sources */, + 945B653E1B5FCEEA0081E995 /* SocketParser.swift in Sources */, + 945B653D1B5FCEEA0081E995 /* SocketPacket.swift in Sources */, + 945B653A1B5FCEEA0081E995 /* SocketFixUTF8.swift in Sources */, + 945B65391B5FCEEA0081E995 /* SocketEventHandler.swift in Sources */, + 94CB8F0B1B6E48B90019ED53 /* SocketTestCases.swift in Sources */, + 945B65371B5FCEEA0081E995 /* SocketEngine.swift in Sources */, + 945B65351B5FCEEA0081E995 /* SocketAckManager.swift in Sources */, + 941A4ABA1B67A56C00C42318 /* TestKind.swift in Sources */, + 94CB8F0D1B6E66E60019ED53 /* AbstractSocketTest.swift in Sources */, + 945B65431B63D9DB0081E995 /* SocketEmitTest.swift in Sources */, + 945B65401B5FCEEA0081E995 /* SwiftRegex.swift in Sources */, + 945B653C1B5FCEEA0081E995 /* SocketLogger.swift in Sources */, + 945B65381B5FCEEA0081E995 /* SocketEngineClient.swift in Sources */, + 945B65361B5FCEEA0081E995 /* SocketAnyEvent.swift in Sources */, + 94ADAC4B1B6632DD00FD79AE /* SocketAcknowledgementTest.swift in Sources */, + 945B653F1B5FCEEA0081E995 /* SocketTypes.swift in Sources */, + 74781D5B1B7E83930042CACA /* SocketIOClientStatus.swift in Sources */, + 945B653B1B5FCEEA0081E995 /* SocketIOClient.swift in Sources */, + 94ADAC491B652D3300FD79AE /* SocketNamespaceEmitTest.swift in Sources */, + 945B65411B5FCEEA0081E995 /* WebSocket.swift in Sources */, + 94242BB81B67B0E500AAAC9D /* SocketNamespaceAcknowledgementTest.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -441,6 +499,7 @@ 5764DF961B51F254004FF46E /* SocketIOClient.swift in Sources */, 5764DF8C1B51F254004FF46E /* SocketAnyEvent.swift in Sources */, 5764DF981B51F254004FF46E /* SocketLogger.swift in Sources */, + 74781D5C1B7E83930042CACA /* SocketIOClientStatus.swift in Sources */, 5764DFA21B51F254004FF46E /* WebSocket.swift in Sources */, 5764DF9A1B51F254004FF46E /* SocketPacket.swift in Sources */, 5764DF8A1B51F254004FF46E /* SocketAckManager.swift in Sources */, @@ -453,6 +512,7 @@ buildActionMask = 2147483647; files = ( 572EF24A1B51F18A00EEBB58 /* SocketIO_MacTests.swift in Sources */, + 74781D5D1B7E83930042CACA /* SocketIOClientStatus.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -485,7 +545,7 @@ isa = XCBuildConfiguration; buildSettings = { PRODUCT_NAME = SocketIO; - SWIFT_WHOLE_MODULE_OPTIMIZATION = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; }; name = Release; }; diff --git a/Socket.IO-Client-Swift.xcodeproj/xcshareddata/xcschemes/SocketIO-iOS.xcscheme b/Socket.IO-Client-Swift.xcodeproj/xcshareddata/xcschemes/SocketIO-iOS.xcscheme index e9b0653..baf02a7 100644 --- a/Socket.IO-Client-Swift.xcodeproj/xcshareddata/xcschemes/SocketIO-iOS.xcscheme +++ b/Socket.IO-Client-Swift.xcodeproj/xcshareddata/xcschemes/SocketIO-iOS.xcscheme @@ -37,10 +37,11 @@ + codeCoverageEnabled = "YES"> @@ -62,15 +63,18 @@ ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj"> + + String { + return rawTestName + testKind!.rawValue + } + + func checkConnectionStatus() { + XCTAssertEqual(socket.status, SocketIOClientStatus.Connected) + XCTAssertFalse(socket.secure) + } + + func socketMultipleEmit(testName:String, emitData:Array, callback:NormalCallback){ + let finalTestname = generateTestName(testName) + weak var expection = self.expectationWithDescription(finalTestname) + func didGetEmit(result:NSArray?, ack:AckEmitter?) { + callback(result, ack) + if let expection = expection { + expection.fulfill() + } + } + + socket.emit(finalTestname, withItems: emitData) + socket.on(finalTestname + "Return", callback: didGetEmit) + waitForExpectationsWithTimeout(SocketEmitTest.TEST_TIMEOUT, handler: nil) + } + + + func socketEmit(testName:String, emitData:AnyObject?, callback:NormalCallback){ + let finalTestname = generateTestName(testName) + weak var expection = self.expectationWithDescription(finalTestname) + func didGetEmit(result:NSArray?, ack:AckEmitter?) { + callback(result, ack) + if let expection = expection { + expection.fulfill() + } + + } + + socket.on(finalTestname + "Return", callback: didGetEmit) + if let emitData = emitData { + socket.emit(finalTestname, emitData) + } else { + socket.emit(finalTestname) + } + + waitForExpectationsWithTimeout(SocketEmitTest.TEST_TIMEOUT, handler: nil) + } + + func socketAcknwoledgeMultiple(testName:String, Data:Array, callback:NormalCallback){ + let finalTestname = generateTestName(testName) + weak var expection = self.expectationWithDescription(finalTestname) + func didGetResult(result:NSArray?) { + callback(result, nil) + if let expection = expection { + expection.fulfill() + } + } + + socket.emitWithAck(finalTestname, withItems: Data)(timeoutAfter: 5, callback: didGetResult) + waitForExpectationsWithTimeout(SocketEmitTest.TEST_TIMEOUT, handler: nil) + } + + func socketAcknwoledge(testName:String, Data:AnyObject?, callback:NormalCallback){ + let finalTestname = generateTestName(testName) + weak var expection = self.expectationWithDescription(finalTestname) + func didGet(result:NSArray?) { + callback(result, nil) + if let expection = expection { + expection.fulfill() + } + } + var ack:OnAckCallback! + if let Data = Data { + ack = socket.emitWithAck(finalTestname, Data) + } else { + ack = socket.emitWithAck(finalTestname) + } + ack(timeoutAfter: 20, callback: didGet) + + waitForExpectationsWithTimeout(SocketEmitTest.TEST_TIMEOUT, handler: nil) + } +} diff --git a/SocketIO-iOSTests/SocketAcknowledgementTest.swift b/SocketIO-iOSTests/SocketAcknowledgementTest.swift new file mode 100644 index 0000000..801e852 --- /dev/null +++ b/SocketIO-iOSTests/SocketAcknowledgementTest.swift @@ -0,0 +1,82 @@ +// +// SocketAcknowledgementTest.swift +// Socket.IO-Client-Swift +// +// Created by Lukas Schmidt on 27.07.15. +// +// + +import XCTest + +class SocketAcknowledgementTest: AbstractSocketTest { + + override func setUp() { + super.setUp() + testKind = TestKind.Acknowledgement + socket = SocketIOClient(socketURL: "127.0.0.1:8080", opts: [ + "reconnects": true, // default true + "reconnectAttempts": -1, // default -1 + "reconnectWait": 5, // default 10 + "forcePolling": false, + "forceWebsockets": false,// default false + "path": ""]) + openConnection() + } + + func testConnectionStatus() { + super.checkConnectionStatus() + } + + func testBasic() { + SocketTestCases.testBasic(socketAcknwoledge) + } + + func testNull() { + SocketTestCases.testNull(socketAcknwoledge) + } + + func testBinary() { + SocketTestCases.testBinary(socketAcknwoledge) + } + + func testArray() { + SocketTestCases.testArray(socketAcknwoledge) + } + + func testString() { + SocketTestCases.testString(socketAcknwoledge) + } + + func testBool() { + SocketTestCases.testBool(socketAcknwoledge) + } + + func testInteger() { + SocketTestCases.testInteger(socketAcknwoledge) + } + + func testDouble() { + SocketTestCases.testDouble(socketAcknwoledge) + } + + func testJSON() { + SocketTestCases.testJSON(socketAcknwoledge) + } + + func testJSONWithBuffer() { + SocketTestCases.testJSONWithBuffer(socketAcknwoledge) + } + + func testUnicode() { + SocketTestCases.testUnicode(socketAcknwoledge) + } + + func testMultipleItems() { + SocketTestCases.testMultipleItems(socketAcknwoledgeMultiple) + } + + func testMultipleWithBuffer() { + SocketTestCases.testMultipleItemsWithBuffer(socketAcknwoledgeMultiple) + } + +} diff --git a/SocketIO-iOSTests/SocketEmitTest.swift b/SocketIO-iOSTests/SocketEmitTest.swift new file mode 100644 index 0000000..a970444 --- /dev/null +++ b/SocketIO-iOSTests/SocketEmitTest.swift @@ -0,0 +1,88 @@ +// +// ConvertedSocketTest.swift +// Socket.IO-Client-Swift +// +// Created by Lukas Schmidt on 25.07.15. +// +// + +import XCTest +import Foundation + +class SocketEmitTest: AbstractSocketTest { + override func setUp() { + super.setUp() + testKind = TestKind.Emit + socket = SocketIOClient(socketURL: "127.0.0.1:8080", opts: [ + "reconnects": true, // default true + "reconnectAttempts": -1, // default -1 + "reconnectWait": 5, // default 10 + "forcePolling": false, + "forceWebsockets": false,// default false + "path": ""] + ) + openConnection() + } + + override func tearDown() { + socket.close(fast: false) + super.tearDown() + } + + func testConnectionStatus() { + super.checkConnectionStatus() + } + + func testBasic() { + SocketTestCases.testBasic(socketEmit) + } + + func testNull() { + SocketTestCases.testNull(socketEmit) + } + + func testBinary() { + SocketTestCases.testBinary(socketEmit) + } + + func testArray() { + SocketTestCases.testArray(socketEmit) + } + + func testString() { + SocketTestCases.testString(socketEmit) + } + + func testBool() { + SocketTestCases.testBool(socketEmit) + } + + func testInteger() { + SocketTestCases.testInteger(socketEmit) + } + + func testDouble() { + SocketTestCases.testDouble(socketEmit) + } + + func testJSON() { + SocketTestCases.testJSON(socketEmit) + } + + func testJSONWithBuffer() { + SocketTestCases.testJSONWithBuffer(socketEmit) + } + + func testUnicode() { + SocketTestCases.testUnicode(socketEmit) + } + + func testMultipleItems() { + SocketTestCases.testMultipleItems(socketMultipleEmit) + } + + func testMultipleWithBuffer() { + SocketTestCases.testMultipleItemsWithBuffer(socketMultipleEmit) + } + +} diff --git a/SocketIO-iOSTests/SocketIO_iOSTests.swift b/SocketIO-iOSTests/SocketIO_iOSTests.swift deleted file mode 100644 index 8e9aad8..0000000 --- a/SocketIO-iOSTests/SocketIO_iOSTests.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// SocketIO_iOSTests.swift -// SocketIO-iOSTests -// -// Created by Nacho Soto on 7/11/15. -// -// - -import UIKit -import XCTest - -class SocketIO_iOSTests: XCTestCase { - - override func setUp() { - super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } - - func testExample() { - // This is an example of a functional test case. - XCTAssert(true, "Pass") - } - - func testPerformanceExample() { - // This is an example of a performance test case. - self.measureBlock() { - // Put the code you want to measure the time of here. - } - } - -} diff --git a/SocketIO-iOSTests/SocketNamespaceAcknowledgementTest.swift b/SocketIO-iOSTests/SocketNamespaceAcknowledgementTest.swift new file mode 100644 index 0000000..d25fecf --- /dev/null +++ b/SocketIO-iOSTests/SocketNamespaceAcknowledgementTest.swift @@ -0,0 +1,82 @@ +// +// SocketNamespaceAcknowledgementTest.swift +// Socket.IO-Client-Swift +// +// Created by Lukas Schmidt on 28.07.15. +// +// + +import XCTest + +class SocketNamespaceAcknowledgementTest: AbstractSocketTest { + + override func setUp() { + super.setUp() + testKind = TestKind.Acknowledgement + socket = SocketIOClient(socketURL: "127.0.0.1:8080", opts: [ + "reconnects": true, // default true + "reconnectAttempts": -1, // default -1 + "reconnectWait": 5, // default 10 + "forcePolling": false, + "forceWebsockets": false,// default false + "path": "", + "nsp": "/swift"]) + openConnection() + } + + func testConnectionStatus() { + super.checkConnectionStatus() + } + + func testBasic() { + SocketTestCases.testBasic(socketAcknwoledge) + } + + func testNull() { + SocketTestCases.testNull(socketAcknwoledge) + } + + func testBinary() { + SocketTestCases.testBinary(socketAcknwoledge) + } + + func testArray() { + SocketTestCases.testArray(socketAcknwoledge) + } + + func testString() { + SocketTestCases.testString(socketAcknwoledge) + } + + func testBool() { + SocketTestCases.testBool(socketAcknwoledge) + } + + func testInteger() { + SocketTestCases.testInteger(socketAcknwoledge) + } + + func testDouble() { + SocketTestCases.testDouble(socketAcknwoledge) + } + + func testJSON() { + SocketTestCases.testJSON(socketAcknwoledge) + } + + func testJSONWithBuffer() { + SocketTestCases.testJSONWithBuffer(socketAcknwoledge) + } + + func testUnicode() { + SocketTestCases.testUnicode(socketAcknwoledge) + } + + func testMultipleItems() { + SocketTestCases.testMultipleItems(socketAcknwoledgeMultiple) + } + + func testMultipleWithBuffer() { + SocketTestCases.testMultipleItemsWithBuffer(socketAcknwoledgeMultiple) + } +} diff --git a/SocketIO-iOSTests/SocketNamespaceEmitTest.swift b/SocketIO-iOSTests/SocketNamespaceEmitTest.swift new file mode 100644 index 0000000..057fcc1 --- /dev/null +++ b/SocketIO-iOSTests/SocketNamespaceEmitTest.swift @@ -0,0 +1,83 @@ +// +// SocketNamespaceEmitTest.swift +// Socket.IO-Client-Swift +// +// Created by Lukas Schmidt on 26.07.15. +// +// + +import XCTest + +class SocketNamespaceEmitTest: AbstractSocketTest { + + override func setUp() { + super.setUp() + testKind = TestKind.Emit + socket = SocketIOClient(socketURL: "127.0.0.1:8080", opts: [ + "reconnects": true, // default true + "reconnectAttempts": -1, // default -1 + "reconnectWait": 5, // default 10 + "forcePolling": false, + "forceWebsockets": false,// default false + "path": "", + "nsp": "/swift"]) + openConnection() + } + + func testConnectionStatus() { + super.checkConnectionStatus() + } + + func testBasic() { + SocketTestCases.testBasic(socketEmit) + } + + func testNull() { + SocketTestCases.testNull(socketEmit) + } + + func testBinary() { + SocketTestCases.testBinary(socketEmit) + } + + func testArray() { + SocketTestCases.testArray(socketEmit) + } + + func testString() { + SocketTestCases.testString(socketEmit) + } + + func testBool() { + SocketTestCases.testBool(socketEmit) + } + + func testInteger() { + SocketTestCases.testInteger(socketEmit) + } + + func testDouble() { + SocketTestCases.testDouble(socketEmit) + } + + func testJSON() { + SocketTestCases.testJSON(socketEmit) + } + + func testJSONWithBuffer() { + SocketTestCases.testJSONWithBuffer(socketEmit) + } + + func testUnicode() { + SocketTestCases.testUnicode(socketEmit) + } + + func testMultipleItems() { + SocketTestCases.testMultipleItems(socketMultipleEmit) + } + + func testMultipleWithBuffer() { + SocketTestCases.testMultipleItemsWithBuffer(socketMultipleEmit) + } + +} diff --git a/SocketIO-iOSTests/SocketTestCases.swift b/SocketIO-iOSTests/SocketTestCases.swift new file mode 100644 index 0000000..b090e7d --- /dev/null +++ b/SocketIO-iOSTests/SocketTestCases.swift @@ -0,0 +1,246 @@ +// +// SocketTestCases.swift +// Socket.IO-Client-Swift +// +// Created by Lukas Schmidt on 02.08.15. +// +// + +import XCTest +import Foundation + +class SocketTestCases: NSObject { + typealias SocketSendFunction = (testName:String, emitData:AnyObject?, callback:NormalCallback)->() + + static func testBasic(abstractSocketSend:SocketSendFunction) { + let testName = "basicTest" + func didGetResult(result:NSArray?, ack:AckEmitter?) { + + } + abstractSocketSend(testName: testName, emitData: nil, callback: didGetResult) + } + + static func testNull(abstractSocketSend:SocketSendFunction) { + let testName = "testNull" + func didGetResult(result:NSArray?, ack:AckEmitter?) { + if let _ = result?.firstObject as? NSNull { + + }else + { + XCTFail("Should have NSNull as result") + } + } + abstractSocketSend(testName: testName, emitData: NSNull(), callback: didGetResult) + } + + static func testBinary(abstractSocketSend:SocketSendFunction) { + let testName = "testBinary" + func didGetResult(result:NSArray?, ack:AckEmitter?) { + if let data = result?.firstObject as? NSData { + let string = NSString(data: data, encoding: NSUTF8StringEncoding)! + XCTAssertEqual(string, "gakgakgak2") + }else { + XCTFail("Should have NSData as result") + } + } + let data = NSString(string: "gakgakgak2").dataUsingEncoding(NSUTF8StringEncoding)! + abstractSocketSend(testName: testName, emitData: data, callback: didGetResult) + } + + static func testArray(abstractSocketSend:SocketSendFunction) { + let testName = "testArray" + func didGetResult(result:NSArray?, ack:AckEmitter?) { + if let array = result?.firstObject as? NSArray { + XCTAssertEqual(array.count, 2) + XCTAssertEqual((array.firstObject! as! String), "test3") + XCTAssertEqual((array.lastObject! as! String), "test4") + }else { + XCTFail("Should have NSArray as result") + } + } + abstractSocketSend(testName: testName, emitData: ["test1", "test2"], callback: didGetResult) + } + + static func testString(abstractSocketSend:SocketSendFunction) { + let testName = "testString" + func didGetResult(result:NSArray?, ack:AckEmitter?) { + if let string = result?.firstObject as? String { + XCTAssertEqual(string, "polo") + }else { + XCTFail("Should have String as result") + } + } + abstractSocketSend(testName: testName, emitData: "marco", callback: didGetResult) + } + + static func testBool(abstractSocketSend:SocketSendFunction) { + let testName = "testBool" + func didGetResult(result:NSArray?, ack:AckEmitter?) { + if let bool = result?.firstObject as? NSNumber { + XCTAssertTrue(bool.boolValue) + }else { + XCTFail("Should have Boolean as result") + } + } + abstractSocketSend(testName: testName, emitData: false, callback: didGetResult) + } + + static func testInteger(abstractSocketSend:SocketSendFunction) { + let testName = "testInteger" + func didGetResult(result:NSArray?, ack:AckEmitter?) { + if let integer = result?.firstObject as? Int { + XCTAssertEqual(integer, 20) + }else { + XCTFail("Should have Integer as result") + } + } + abstractSocketSend(testName: testName, emitData: 10, callback: didGetResult) + } + + static func testDouble(abstractSocketSend:SocketSendFunction) { + let testName = "testDouble" + func didGetResult(result:NSArray?, ack:AckEmitter?) { + if let double = result?.firstObject as? NSNumber { + XCTAssertEqual(double.floatValue, 1.2) + }else { + XCTFail("Should have Double as result") + } + } + abstractSocketSend(testName: testName, emitData: 1.1, callback: didGetResult) + } + + static func testJSONWithBuffer(abstractSocketSend:SocketSendFunction) { + let testName = "testJSONWithBuffer" + func didGetResult(result:NSArray?, ack:AckEmitter?) { + if let json = result?.firstObject as? NSDictionary { + XCTAssertEqual((json.valueForKey("testString")! as! String), "test") + XCTAssertEqual((json.valueForKey("testNumber")! as! Int), 15) + XCTAssertEqual((json.valueForKey("testArray")! as! Array).count, 2) + XCTAssertEqual(((json.valueForKey("testArray")! as! Array).last! as! Int), 1) + let string = NSString(data: (json.valueForKey("testArray")! as! Array).first! as! NSData, encoding: NSUTF8StringEncoding)! + XCTAssertEqual(string, "gakgakgak2") + }else { + XCTFail("Should have NSDictionary as result") + } + } + let json = ["name": "test", "testArray": ["hallo"], "nestedTest": ["test": "test"], "number": 15] + + abstractSocketSend(testName: testName, emitData: json, callback: didGetResult) + } + + static func testJSON(abstractSocketSend:SocketSendFunction) { + let testName = "testJSON" + func didGetResult(result:NSArray?, ack:AckEmitter?) { + if let json = result?.firstObject as? NSDictionary { + XCTAssertEqual((json.valueForKey("testString")! as! String), "test") + XCTAssertEqual(json.valueForKey("testNumber")! as? Int, 15) + XCTAssertEqual((json.valueForKey("testArray")! as! Array).count, 2) + XCTAssertEqual((json.valueForKey("testArray")! as! Array).first! as? Int, 1) + XCTAssertEqual((json.valueForKey("testArray")! as! Array).last! as? Int, 1) + + }else { + XCTFail("Should have NSDictionary as result") + } + } + let json = ["name": "test", "testArray": ["hallo"], "nestedTest": ["test": "test"], "number": 15] + + abstractSocketSend(testName: testName, emitData: json, callback: didGetResult) + } + + static func testUnicode(abstractSocketSend:SocketSendFunction) { + let testName = "testUnicode" + func didGetResult(result:NSArray?, ack:AckEmitter?) { + if let unicode = result?.firstObject as? String { + XCTAssertEqual(unicode, "🚄") + }else { + XCTFail("Should have String as result") + } + } + abstractSocketSend(testName: testName, emitData: "🚀", callback: didGetResult) + } + + static func testMultipleItemsWithBuffer(abstractSocketMultipleSend:(testName:String, emitData:Array, callback:NormalCallback)->()) { + let testName = "testMultipleItemsWithBuffer" + func didGetResult(result:NSArray?, ack:AckEmitter?) { + XCTAssertEqual(result!.count, 5) + if result!.count != 5 { + XCTFail("Fatal Fail. Lost some Data") + return + } + if let array = result?.firstObject as? Array { + XCTAssertEqual((array.last! as! Int), 2) + XCTAssertEqual((array.first! as! Int), 1) + }else { + XCTFail("Should have Array as result") + } + if let dict = result?[1] as? NSDictionary { + XCTAssertEqual((dict.valueForKey("test") as! String), "bob") + + }else { + XCTFail("Should have NSDictionary as result") + } + if let number = result?[2] as? Int { + XCTAssertEqual(number, 25) + + }else { + XCTFail("Should have Integer as result") + } + if let string = result?[3] as? String { + XCTAssertEqual(string, "polo") + + }else { + XCTFail("Should have Integer as result") + } + if let data = result?[4] as? NSData { + let string = NSString(data: data, encoding: NSUTF8StringEncoding)! + XCTAssertEqual(string, "gakgakgak2") + }else { + XCTFail("Should have NSData as result") + } + } + let data = NSString(string: "gakgakgak2").dataUsingEncoding(NSUTF8StringEncoding)! + let emitArray = [["test1", "test2"], ["test": "test"], 15, "marco", data] + abstractSocketMultipleSend(testName: testName, emitData: emitArray, callback: didGetResult) + } + + static func testMultipleItems(abstractSocketMultipleSend:(testName:String, emitData:Array, callback:NormalCallback)->()) { + let testName = "testMultipleItems" + func didGetResult(result:NSArray?, ack:AckEmitter?) { + XCTAssertEqual(result!.count, 5) + if result!.count != 5 { + XCTFail("Fatal Fail. Lost some Data") + return + } + if let array = result?.firstObject as? Array { + XCTAssertEqual((array.last! as! Int), 2) + XCTAssertEqual((array.first! as! Int), 1) + }else { + XCTFail("Should have Array as result") + } + if let dict = result?[1] as? NSDictionary { + XCTAssertEqual((dict.valueForKey("test") as! String), "bob") + + }else { + XCTFail("Should have NSDictionary as result") + } + if let number = result?[2] as? Int { + XCTAssertEqual(number, 25) + + }else { + XCTFail("Should have Integer as result") + } + if let string = result?[3] as? String { + XCTAssertEqual(string, "polo") + }else { + XCTFail("Should have Integer as result") + } + if let bool = result?[4] as? NSNumber { + XCTAssertFalse(bool.boolValue) + }else { + XCTFail("Should have NSNumber as result") + } + } + let emitArray = [["test1", "test2"], ["test": "test"], 15, "marco", false] + abstractSocketMultipleSend(testName: testName, emitData: emitArray, callback: didGetResult) + } +} diff --git a/SocketIO-iOSTests/TestKind.swift b/SocketIO-iOSTests/TestKind.swift new file mode 100644 index 0000000..d83c59b --- /dev/null +++ b/SocketIO-iOSTests/TestKind.swift @@ -0,0 +1,13 @@ +// +// TestKind.swift +// Socket.IO-Client-Swift +// +// Created by Lukas Schmidt on 28.07.15. +// +// + +import Foundation + +enum TestKind: String { + case Emit, Acknowledgement +} \ No newline at end of file diff --git a/SocketIOClientSwift/SocketAckManager.swift b/SocketIOClientSwift/SocketAckManager.swift index a68d247..c707a23 100644 --- a/SocketIOClientSwift/SocketAckManager.swift +++ b/SocketIOClientSwift/SocketAckManager.swift @@ -1,6 +1,6 @@ // // SocketAckManager.swift -// SocketIO-Swift +// Socket.IO-Client-Swift // // Created by Erik Little on 4/3/15. // @@ -25,38 +25,38 @@ import Foundation private struct SocketAck: Hashable, Equatable { - let ack:Int - var callback:AckCallback! - var hashValue:Int { + let ack: Int + var callback: AckCallback! + var hashValue: Int { return ack.hashValue } - init(ack:Int) { + init(ack: Int) { self.ack = ack } - init(ack:Int, callback:AckCallback) { + init(ack: Int, callback: AckCallback) { self.ack = ack self.callback = callback } } -private func <(lhs:SocketAck, rhs:SocketAck) -> Bool { +private func <(lhs: SocketAck, rhs: SocketAck) -> Bool { return lhs.ack < rhs.ack } -private func ==(lhs:SocketAck, rhs:SocketAck) -> Bool { +private func ==(lhs: SocketAck, rhs: SocketAck) -> Bool { return lhs.ack == rhs.ack } struct SocketAckManager { private var acks = Set(minimumCapacity: 1) - mutating func addAck(ack:Int, callback:AckCallback) { + mutating func addAck(ack: Int, callback: AckCallback) { acks.insert(SocketAck(ack: ack, callback: callback)) } - mutating func executeAck(ack:Int, items:[AnyObject]?) { + mutating func executeAck(ack: Int, items: [AnyObject]?) { let callback = acks.remove(SocketAck(ack: ack)) dispatch_async(dispatch_get_main_queue()) { @@ -64,7 +64,7 @@ struct SocketAckManager { } } - mutating func timeoutAck(ack:Int) { + mutating func timeoutAck(ack: Int) { let callback = acks.remove(SocketAck(ack: ack)) dispatch_async(dispatch_get_main_queue()) { diff --git a/SocketIOClientSwift/SocketAnyEvent.swift b/SocketIOClientSwift/SocketAnyEvent.swift index 6d7228c..fdaa315 100644 --- a/SocketIOClientSwift/SocketAnyEvent.swift +++ b/SocketIOClientSwift/SocketAnyEvent.swift @@ -1,6 +1,6 @@ // // SocketAnyEvent.swift -// Socket.IO-Swift +// Socket.IO-Client-Swift // // Created by Erik Little on 3/28/15. // @@ -24,11 +24,14 @@ import Foundation -@objc public final class SocketAnyEvent { - public let event:String! - public let items:NSArray? +@objc public final class SocketAnyEvent: NSObject { + public let event: String! + public let items: NSArray? + override public var description: String { + return "SocketAnyEvent: Event: \(event) items: \(items ?? nil)" + } - init(event:String, items:NSArray?) { + init(event: String, items: NSArray?) { self.event = event self.items = items } diff --git a/SocketIOClientSwift/SocketEngine.swift b/SocketIOClientSwift/SocketEngine.swift index 86f4b5c..4e0a5bf 100644 --- a/SocketIOClientSwift/SocketEngine.swift +++ b/SocketIOClientSwift/SocketEngine.swift @@ -1,6 +1,6 @@ // // SocketEngine.swift -// Socket.IO-Swift +// Socket.IO-Client-Swift // // Created by Erik Little on 3/3/15. // @@ -25,24 +25,23 @@ import Foundation public final class SocketEngine: NSObject, WebSocketDelegate, SocketLogClient { - private typealias Probe = (msg:String, type:PacketType, data:ContiguousArray?) + private typealias Probe = (msg: String, type: PacketType, data: [NSData]?) private typealias ProbeWaitQueue = [Probe] - + private let allowedCharacterSet = NSCharacterSet(charactersInString: "!*'();:@&=+$,/?%#[]\" {}").invertedSet private let workQueue = NSOperationQueue() private let emitQueue = dispatch_queue_create("engineEmitQueue", DISPATCH_QUEUE_SERIAL) private let parseQueue = dispatch_queue_create("engineParseQueue", DISPATCH_QUEUE_SERIAL) private let handleQueue = dispatch_queue_create("engineHandleQueue", DISPATCH_QUEUE_SERIAL) - private let session:NSURLSession! - + private let session: NSURLSession! + private var closed = false - private var _connected = false - private var extraHeaders:[String: String]? + private var extraHeaders: [String: String]? private var fastUpgrade = false private var forcePolling = false private var forceWebsockets = false - private var pingInterval:Double? - private var pingTimer:NSTimer? + private var pingInterval: Double? + private var pingTimer: NSTimer? private var pingTimeout = 0.0 { didSet { pongsMissedMax = Int(pingTimeout / (pingInterval ?? 25)) @@ -51,117 +50,102 @@ public final class SocketEngine: NSObject, WebSocketDelegate, SocketLogClient { private var pongsMissed = 0 private var pongsMissedMax = 0 private var postWait = [String]() - private var _polling = true private var probing = false private var probeWait = ProbeWaitQueue() private var waitingForPoll = false private var waitingForPost = false - private var _websocket = false private var websocketConnected = false - + let logType = "SocketEngine" - - var connected:Bool { - return _connected - } - weak var client:SocketEngineClient? - var cookies:[NSHTTPCookie]? - var log = false - var polling:Bool { - return _polling - } + + private(set) var connected = false + private(set) var polling = true + private(set) var websocket = false + + weak var client: SocketEngineClient? + var cookies: [NSHTTPCookie]? var sid = "" var socketPath = "" - var urlPolling:String? - var urlWebSocket:String? - var websocket:Bool { - return _websocket - } + var urlPolling: String? + var urlWebSocket: String? var ws:WebSocket? - - public enum PacketType:Int { - case OPEN = 0 - case CLOSE = 1 - case PING = 2 - case PONG = 3 - case MESSAGE = 4 - case UPGRADE = 5 - case NOOP = 6 - - init?(str:String?) { - if let value = str?.toInt(), raw = PacketType(rawValue: value) { + + public enum PacketType: Int { + case Open, Close, Ping, Pong, Message, Upgrade, Noop + + init?(str: String?) { + if let value = Int(str ?? ""), raw = PacketType(rawValue: value) { self = raw } else { return nil } } } - - public init(client:SocketEngineClient, sessionDelegate:NSURLSessionDelegate?) { + + public init(client: SocketEngineClient, sessionDelegate: NSURLSessionDelegate?) { self.client = client - self.session = NSURLSession(configuration: NSURLSessionConfiguration.ephemeralSessionConfiguration(), + self.session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration(), delegate: sessionDelegate, delegateQueue: workQueue) } - - public convenience init(client:SocketEngineClient, opts:NSDictionary?) { + + public convenience init(client: SocketEngineClient, opts: NSDictionary?) { self.init(client: client, sessionDelegate: opts?["sessionDelegate"] as? NSURLSessionDelegate) forceWebsockets = opts?["forceWebsockets"] as? Bool ?? false forcePolling = opts?["forcePolling"] as? Bool ?? false cookies = opts?["cookies"] as? [NSHTTPCookie] - log = opts?["log"] as? Bool ?? false socketPath = opts?["path"] as? String ?? "" extraHeaders = opts?["extraHeaders"] as? [String: String] } - + deinit { SocketLogger.log("Engine is being deinit", client: self) } - - public func close(#fast:Bool) { + + public func close(fast fast: Bool) { SocketLogger.log("Engine is being closed. Fast: %@", client: self, args: fast) - + pingTimer?.invalidate() closed = true - + ws?.disconnect() - + if fast || polling { - write("", withType: PacketType.CLOSE, withData: nil) + write("", withType: PacketType.Close, withData: nil) client?.engineDidClose("Disconnect") } - + stopPolling() } - - private func createBinaryDataForSend(data:NSData) -> (NSData?, String?) { + + private func createBinaryDataForSend(data: NSData) -> (NSData?, String?) { if websocket { var byteArray = [UInt8](count: 1, repeatedValue: 0x0) byteArray[0] = 4 - var mutData = NSMutableData(bytes: &byteArray, length: 1) - + let mutData = NSMutableData(bytes: &byteArray, length: 1) + mutData.appendData(data) - + return (mutData, nil) } else { var str = "b4" str += data.base64EncodedStringWithOptions( NSDataBase64EncodingOptions.Encoding64CharacterLineLength) - + return (nil, str) } } - - private func createURLs(params:[String: AnyObject]?) -> (String?, String?) { + + private func createURLs(params: [String: AnyObject]?) -> (String?, String?) { if client == nil { return (nil, nil) } - + let path = socketPath == "" ? "/socket.io" : socketPath - - var url = "\(client!.socketURL)\(path)/?transport=" - var urlPolling:String - var urlWebSocket:String - + + let url = "\(client!.socketURL)\(path)/?transport=" + var urlPolling: String + var urlWebSocket: String + if client!.secure { urlPolling = "https://" + url + "polling" urlWebSocket = "wss://" + url + "websocket" @@ -169,15 +153,15 @@ public final class SocketEngine: NSObject, WebSocketDelegate, SocketLogClient { urlPolling = "http://" + url + "polling" urlWebSocket = "ws://" + url + "websocket" } - + if params != nil { - + for (key, value) in params! { let keyEsc = key.stringByAddingPercentEncodingWithAllowedCharacters( allowedCharacterSet)! urlPolling += "&\(keyEsc)=" urlWebSocket += "&\(keyEsc)=" - + if value is String { let valueEsc = (value as! String).stringByAddingPercentEncodingWithAllowedCharacters( allowedCharacterSet)! @@ -189,11 +173,11 @@ public final class SocketEngine: NSObject, WebSocketDelegate, SocketLogClient { } } } - + return (urlPolling, urlWebSocket) } - - private func createWebsocket(andConnect connect:Bool) { + + private func createWebsocket(andConnect connect: Bool) { let wsUrl = urlWebSocket! + (sid == "" ? "" : "&sid=\(sid)") ws = WebSocket(url: NSURL(string: wsUrl)!, @@ -207,34 +191,34 @@ public final class SocketEngine: NSObject, WebSocketDelegate, SocketLogClient { ws?.queue = handleQueue ws?.delegate = self - + if connect { ws?.connect() } } - + private func doFastUpgrade() { if waitingForPoll { SocketLogger.err("Outstanding poll when switched to WebSockets," + "we'll probably disconnect soon. You should report this.", client: self) } - - sendWebSocketMessage("", withType: PacketType.UPGRADE, datas: nil) - _websocket = true - _polling = false + + sendWebSocketMessage("", withType: PacketType.Upgrade, datas: nil) + websocket = true + polling = false fastUpgrade = false probing = false flushProbeWait() } - + private func doPoll() { if websocket || waitingForPoll || !connected { return } - + waitingForPoll = true let req = NSMutableURLRequest(URL: NSURL(string: urlPolling! + "&sid=\(sid)&b64=1")!) - + if cookies != nil { let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies!) req.allHTTPHeaderFields = headers @@ -248,37 +232,37 @@ public final class SocketEngine: NSObject, WebSocketDelegate, SocketLogClient { doRequest(req) } - - private func doRequest(req:NSMutableURLRequest) { + + private func doRequest(req: NSMutableURLRequest) { if !polling { return } - + req.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData - + SocketLogger.log("Doing polling request", client: self) - + session.dataTaskWithRequest(req) {[weak self] data, res, err in if let this = self { - if err != nil { + if err != nil || data == nil { if this.polling { - this.handlePollingFailed(err.localizedDescription) + this.handlePollingFailed(err?.localizedDescription ?? "Error") } else { - SocketLogger.err(err.localizedDescription, client: this) + SocketLogger.err(err?.localizedDescription ?? "Error", client: this) } return } - + SocketLogger.log("Got polling response", client: this) - - if let str = NSString(data: data, encoding: NSUTF8StringEncoding) as? String { + + if let str = NSString(data: data!, encoding: NSUTF8StringEncoding) as? String { dispatch_async(this.parseQueue) {[weak this] in this?.parsePollingMessage(str) } } - + this.waitingForPoll = false - + if this.fastUpgrade { this.doFastUpgrade() } else if !this.closed && this.polling { @@ -286,25 +270,25 @@ public final class SocketEngine: NSObject, WebSocketDelegate, SocketLogClient { } }}.resume() } - + private func flushProbeWait() { SocketLogger.log("Flushing probe wait", client: self) - + dispatch_async(emitQueue) {[weak self] in if let this = self { for waiter in this.probeWait { this.write(waiter.msg, withType: waiter.type, withData: waiter.data) } - + this.probeWait.removeAll(keepCapacity: false) - + if this.postWait.count != 0 { this.flushWaitingForPostToWebSocket() } } } } - + private func flushWaitingForPost() { if postWait.count == 0 || !connected { return @@ -312,49 +296,49 @@ public final class SocketEngine: NSObject, WebSocketDelegate, SocketLogClient { flushWaitingForPostToWebSocket() return } - + var postStr = "" - + for packet in postWait { - let len = count(packet) - + let len = packet.characters.count + postStr += "\(len):\(packet)" } - + postWait.removeAll(keepCapacity: false) - + let req = NSMutableURLRequest(URL: NSURL(string: urlPolling! + "&sid=\(sid)")!) - - if cookies != nil { - let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies!) + + if let cookies = cookies { + let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies) req.allHTTPHeaderFields = headers } - + req.HTTPMethod = "POST" req.setValue("text/plain; charset=UTF-8", forHTTPHeaderField: "Content-Type") - + let postData = postStr.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! - + req.HTTPBody = postData req.setValue(String(postData.length), forHTTPHeaderField: "Content-Length") - + waitingForPost = true - + SocketLogger.log("POSTing: %@", client: self, args: postStr) - + session.dataTaskWithRequest(req) {[weak self] data, res, err in if let this = self { if err != nil && this.polling { - this.handlePollingFailed(err.localizedDescription) + this.handlePollingFailed(err?.localizedDescription ?? "Error") return } else if err != nil { - NSLog(err.localizedDescription) + NSLog(err?.localizedDescription ?? "Error") return } - + this.waitingForPost = false - + dispatch_async(this.emitQueue) {[weak this] in if !(this?.fastUpgrade ?? true) { this?.flushWaitingForPost() @@ -363,253 +347,250 @@ public final class SocketEngine: NSObject, WebSocketDelegate, SocketLogClient { } }}.resume() } - + // We had packets waiting for send when we upgraded // Send them raw private func flushWaitingForPostToWebSocket() { for msg in postWait { ws?.writeString(msg) } - + postWait.removeAll(keepCapacity: true) } - + private func handleClose() { - if polling { - client?.engineDidClose("Disconnect") + if let client = client where polling == true { + client.engineDidClose("Disconnect") } } - - private func checkIfMessageIsBase64Binary(var message:String) { + + private func checkIfMessageIsBase64Binary(var message: String) { if message.hasPrefix("b4") { // binary in base64 string message.removeRange(Range(start: message.startIndex, - end: advance(message.startIndex, 2))) - + end: message.startIndex.advancedBy(2))) + if let data = NSData(base64EncodedString: message, - options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters), client = client { - dispatch_async(client.handleQueue) {[weak self] in - self?.client?.parseBinaryData(data) - } + options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters) { + client?.parseBinaryData(data) } } } - - private func handleMessage(message:String) { - if let client = client { - dispatch_async(client.handleQueue) {[weak client] in - client?.parseSocketMessage(message) - } - } + + private func handleMessage(message: String) { + client?.parseSocketMessage(message) } - + private func handleNOOP() { doPoll() } - - private func handleOpen(openData:String) { - var err:NSError? + + private func handleOpen(openData: String) { let mesData = openData.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! - - if let json = NSJSONSerialization.JSONObjectWithData(mesData, - options: NSJSONReadingOptions.AllowFragments, - error: &err) as? NSDictionary, sid = json["sid"] as? String { + do { + let json = try NSJSONSerialization.JSONObjectWithData(mesData, + options: NSJSONReadingOptions.AllowFragments) as? NSDictionary + if let sid = json?["sid"] as? String { + let upgradeWs: Bool + self.sid = sid - _connected = true + connected = true - if !forcePolling && !forceWebsockets { - createWebsocket(andConnect: true) + if let upgrades = json?["upgrades"] as? [String] { + upgradeWs = upgrades.filter {$0 == "websocket"}.count != 0 + } else { + upgradeWs = false } - if let pingInterval = json["pingInterval"] as? Double, pingTimeout = json["pingTimeout"] as? Double { + if let pingInterval = json?["pingInterval"] as? Double, pingTimeout = json?["pingTimeout"] as? Double { self.pingInterval = pingInterval / 1000.0 self.pingTimeout = pingTimeout / 1000.0 } - } else { - client?.didError("Engine failed to handshake") + + if !forcePolling && !forceWebsockets && upgradeWs { + createWebsocket(andConnect: true) + } + } + } catch { + SocketLogger.err("Error parsing open packet", client: self) return } - + startPingTimer() - + if !forceWebsockets { doPoll() } } - - private func handlePong(pongMessage:String) { + + private func handlePong(pongMessage: String) { pongsMissed = 0 - + // We should upgrade if pongMessage == "3probe" { upgradeTransport() } } - + // A poll failed, tell the client about it - private func handlePollingFailed(reason:String) { - _connected = false + private func handlePollingFailed(reason: String) { + connected = false ws?.disconnect() pingTimer?.invalidate() waitingForPoll = false waitingForPost = false - + // If cancelled we were already closing if client == nil || reason == "cancelled" { return } - + if !closed { client?.didError(reason) client?.engineDidClose(reason) } } - - public func open(opts:[String: AnyObject]? = nil) { + + public func open(opts: [String: AnyObject]? = nil) { if connected { SocketLogger.err("Tried to open while connected", client: self) - client?.didError("Tried to open while connected") + return } - + SocketLogger.log("Starting engine", client: self) SocketLogger.log("Handshaking", client: self) - + closed = false - + (urlPolling, urlWebSocket) = createURLs(opts) - + if forceWebsockets { - _polling = false - _websocket = true + polling = false + websocket = true createWebsocket(andConnect: true) return } - + let reqPolling = NSMutableURLRequest(URL: NSURL(string: urlPolling! + "&b64=1")!) - + if cookies != nil { let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies!) reqPolling.allHTTPHeaderFields = headers } - - if extraHeaders != nil { - for (headerName, value) in extraHeaders! { + + if let extraHeaders = extraHeaders { + for (headerName, value) in extraHeaders { reqPolling.setValue(value, forHTTPHeaderField: headerName) } } doRequest(reqPolling) } - + // Translatation of engine.io-parser#decodePayload private func parsePollingMessage(str:String) { - if count(str) == 1 { + guard str.characters.count != 1 else { return } - // println(str) - - let strArray = Array(str) + + let strArray = Array(str.characters) var length = "" var n = 0 var msg = "" - + func testLength(length:String, inout n:Int) -> Bool { - if let num = length.toInt() { + if let num = Int(length) { n = num return false } else { return true } } - - for var i = 0, l = count(str); i < l; i++ { + + for var i = 0, l = str.characters.count; i < l; i++ { let chr = String(strArray[i]) - + if chr != ":" { length += chr } else { - if length == "" || testLength(length, &n) { + if length == "" || testLength(length, n: &n) { SocketLogger.err("Parsing error: %@", client: self, args: str) handlePollingFailed("Error parsing XHR message") return } - + msg = String(strArray[i+1...i+n]) - - if let lengthInt = length.toInt() where lengthInt != count(msg) { + + if let lengthInt = Int(length) where lengthInt != msg.characters.count { SocketLogger.err("Parsing error: %@", client: self, args: str) return } - - if count(msg) != 0 { + + if msg.characters.count != 0 { // Be sure to capture the value of the msg dispatch_async(handleQueue) {[weak self, msg] in self?.parseEngineMessage(msg, fromPolling: true) } } - + i += n length = "" } } } - - private func parseEngineData(data:NSData) { - if let client = client { - dispatch_async(client.handleQueue) {[weak self] in - self?.client?.parseBinaryData(data.subdataWithRange(NSMakeRange(1, data.length - 1))) - } - } + + private func parseEngineData(data: NSData) { + client?.parseBinaryData(data.subdataWithRange(NSMakeRange(1, data.length - 1))) } - - private func parseEngineMessage(var message:String, fromPolling:Bool) { + + private func parseEngineMessage(var message: String, fromPolling: Bool) { SocketLogger.log("Got message: %@", client: self, args: message) - + if fromPolling { fixDoubleUTF8(&message) } - + let type = PacketType(str: (message["^(\\d)"].groups()?[1])) ?? { self.checkIfMessageIsBase64Binary(message) - return PacketType.NOOP - }() - + return PacketType.Noop + }() + switch type { - case PacketType.MESSAGE: + case PacketType.Message: message.removeAtIndex(message.startIndex) handleMessage(message) - case PacketType.NOOP: + case PacketType.Noop: handleNOOP() - case PacketType.PONG: + case PacketType.Pong: handlePong(message) - case PacketType.OPEN: + case PacketType.Open: message.removeAtIndex(message.startIndex) handleOpen(message) - case PacketType.CLOSE: + case PacketType.Close: handleClose() default: SocketLogger.log("Got unknown packet type", client: self) } } - + private func probeWebSocket() { if websocketConnected { - sendWebSocketMessage("probe", withType: PacketType.PING) + sendWebSocketMessage("probe", withType: PacketType.Ping) } } - + /// Send an engine message (4) - public func send(msg:String, withData datas:ContiguousArray?) { + public func send(msg: String, withData datas: [NSData]?) { if probing { - probeWait.append((msg, PacketType.MESSAGE, datas)) + probeWait.append((msg, PacketType.Message, datas)) } else { - write(msg, withType: PacketType.MESSAGE, withData: datas) + write(msg, withType: PacketType.Message, withData: datas) } } - + @objc private func sendPing() { //Server is not responding if pongsMissed > pongsMissedMax { @@ -617,59 +598,59 @@ public final class SocketEngine: NSObject, WebSocketDelegate, SocketLogClient { client?.engineDidClose("Ping timeout") return } - + ++pongsMissed - write("", withType: PacketType.PING, withData: nil) + write("", withType: PacketType.Ping, withData: nil) } - + /// Send polling message. /// Only call on emitQueue - private func sendPollMessage(var msg:String, withType type:PacketType, - datas:ContiguousArray? = nil) { + private func sendPollMessage(var msg: String, withType type: PacketType, + datas:[NSData]? = nil) { SocketLogger.log("Sending poll: %@ as type: %@", client: self, args: msg, type.rawValue) - + doubleEncodeUTF8(&msg) let strMsg = "\(type.rawValue)\(msg)" - + postWait.append(strMsg) - - if datas != nil { - for data in datas! { - let (nilData, b64Data) = createBinaryDataForSend(data) - + + if let datas = datas { + for data in datas { + let (_, b64Data) = createBinaryDataForSend(data) + postWait.append(b64Data!) } } - + if !waitingForPost { flushWaitingForPost() } } - + /// Send message on WebSockets /// Only call on emitQueue - private func sendWebSocketMessage(str:String, withType type:PacketType, - datas:ContiguousArray? = nil) { + private func sendWebSocketMessage(str: String, withType type: PacketType, + datas:[NSData]? = nil) { SocketLogger.log("Sending ws: %@ as type: %@", client: self, args: str, type.rawValue) - + ws?.writeString("\(type.rawValue)\(str)") - - if datas != nil { - for data in datas! { - let (data, nilString) = createBinaryDataForSend(data) + + if let datas = datas { + for data in datas { + let (data, _) = createBinaryDataForSend(data) if data != nil { ws?.writeData(data!) } } } } - + // Starts the ping timer private func startPingTimer() { - if pingInterval == nil { + guard pingInterval != nil else { return } - + pingTimer?.invalidate() dispatch_async(dispatch_get_main_queue()) {[weak self] in if let this = self { @@ -678,25 +659,25 @@ public final class SocketEngine: NSObject, WebSocketDelegate, SocketLogClient { } } } - + func stopPolling() { session.invalidateAndCancel() } - + private func upgradeTransport() { if websocketConnected { SocketLogger.log("Upgrading transport to WebSockets", client: self) - + fastUpgrade = true - sendPollMessage("", withType: PacketType.NOOP) + sendPollMessage("", withType: PacketType.Noop) // After this point, we should not send anymore polling messages } } - + /** Write a message, independent of transport. */ - public func write(msg:String, withType type:PacketType, withData data:ContiguousArray?) { + public func write(msg: String, withType type: PacketType, withData data: [NSData]?) { dispatch_async(emitQueue) {[weak self] in if let this = self where this.connected { if this.websocket { @@ -711,55 +692,55 @@ public final class SocketEngine: NSObject, WebSocketDelegate, SocketLogClient { } } } - + /** Write a message, independent of transport. For Objective-C. withData should be an NSArray of NSData */ - public func writeObjc(msg:String, withType type:Int, withData data:NSArray?) { + public func writeObjc(msg: String, withType type: Int, withData data: NSArray?) { if let pType = PacketType(rawValue: type) { - var arr = ContiguousArray() - - if data != nil { - for d in data! { + var arr = [NSData]() + + if let data = data { + for d in data { arr.append(d as! NSData) } } - + write(msg, withType: pType, withData: arr) } } - + // Delagate methods - + public func websocketDidConnect(socket:WebSocket) { websocketConnected = true - + if !forceWebsockets { probing = true probeWebSocket() } else { - _connected = true + connected = true probing = false - _polling = false + polling = false } } - - public func websocketDidDisconnect(socket:WebSocket, error:NSError?) { + + public func websocketDidDisconnect(socket: WebSocket, error: NSError?) { websocketConnected = false probing = false - + if closed { client?.engineDidClose("Disconnect") return } - + if websocket { pingTimer?.invalidate() - _connected = false - _websocket = false - + connected = false + websocket = false + let reason = error?.localizedDescription ?? "Socket Disconnected" - + if error != nil { client?.didError(reason) } @@ -769,12 +750,12 @@ public final class SocketEngine: NSObject, WebSocketDelegate, SocketLogClient { flushProbeWait() } } - - public func websocketDidReceiveMessage(socket:WebSocket, text:String) { + + public func websocketDidReceiveMessage(socket: WebSocket, text: String) { parseEngineMessage(text, fromPolling: false) } - - public func websocketDidReceiveData(socket:WebSocket, data:NSData) { + + public func websocketDidReceiveData(socket: WebSocket, data: NSData) { parseEngineData(data) } } diff --git a/SocketIOClientSwift/SocketEngineClient.swift b/SocketIOClientSwift/SocketEngineClient.swift index 27040fb..bc0f4ae 100644 --- a/SocketIOClientSwift/SocketEngineClient.swift +++ b/SocketIOClientSwift/SocketEngineClient.swift @@ -1,6 +1,6 @@ // // SocketEngineClient.swift -// Socket.IO-Swift +// Socket.IO-Client-Swift // // Created by Erik Little on 3/19/15. // @@ -26,12 +26,11 @@ import Foundation @objc public protocol SocketEngineClient { - var handleQueue:dispatch_queue_attr_t! {get} - var socketURL:String {get} - var secure:Bool {get} + var socketURL: String {get} + var secure: Bool {get} - func didError(reason:AnyObject) - func engineDidClose(reason:String) - func parseSocketMessage(msg:String) - func parseBinaryData(data:NSData) + func didError(reason: AnyObject) + func engineDidClose(reason: String) + func parseSocketMessage(msg: String) + func parseBinaryData(data: NSData) } diff --git a/SocketIOClientSwift/SocketEventHandler.swift b/SocketIOClientSwift/SocketEventHandler.swift index cef0d4d..05efb10 100644 --- a/SocketIOClientSwift/SocketEventHandler.swift +++ b/SocketIOClientSwift/SocketEventHandler.swift @@ -1,6 +1,6 @@ // // EventHandler.swift -// Socket.IO-Swift +// Socket.IO-Client-Swift // // Created by Erik Little on 1/18/15. // @@ -24,24 +24,37 @@ import Foundation -private func emitAckCallback(socket:SocketIOClient, num:Int) - (items:AnyObject...) -> Void { - socket.emitAck(num, withData: items) +private func emitAckCallback(socket: SocketIOClient?, num: Int?) + (items: AnyObject...) -> Void { + socket?.emitAck(num ?? -1, withItems: items) } -final class SocketEventHandler { - let event:String! - let callback:NormalCallback? +private func emitAckCallbackObjectiveC(socket: SocketIOClient?, num: Int?) + (items: NSArray) -> Void { + socket?.emitAck(num ?? -1, withItems: items as [AnyObject]) +} + +struct SocketEventHandler { + let event: String + let callback: NormalCallback? + let callBackObjectiveC: NormalCallbackObjectiveC? - init(event:String, callback:NormalCallback) { + init(event: String, callback: NormalCallback) { self.event = event self.callback = callback + self.callBackObjectiveC = nil } - func executeCallback(_ items:NSArray? = nil, withAck ack:Int? = nil, withAckType type:Int? = nil, + init(event: String, callback: NormalCallbackObjectiveC) { + self.event = event + self.callback = nil + self.callBackObjectiveC = callback + } + + func executeCallback(items:NSArray? = nil, withAck ack:Int? = nil, withAckType type:Int? = nil, withSocket socket:SocketIOClient? = nil) { - dispatch_async(dispatch_get_main_queue()) {[weak self] in - self?.callback?(items, ack != nil ? emitAckCallback(socket!, ack!) : nil) - } + self.callback != nil ? + self.callback?(items, emitAckCallback(socket, num: ack)) + : self.callBackObjectiveC?(items, emitAckCallbackObjectiveC(socket, num: ack)) } } diff --git a/SocketIOClientSwift/SocketFixUTF8.swift b/SocketIOClientSwift/SocketFixUTF8.swift index c8ea8ef..0cffd86 100644 --- a/SocketIOClientSwift/SocketFixUTF8.swift +++ b/SocketIOClientSwift/SocketFixUTF8.swift @@ -1,6 +1,6 @@ // // SocketFixUTF8.swift -// Socket.IO-Swift +// Socket.IO-Client-Swift // // Created by Erik Little on 3/16/15. // @@ -25,13 +25,13 @@ import Foundation -func fixDoubleUTF8(inout name:String) { +func fixDoubleUTF8(inout name: String) { let utf8 = name.dataUsingEncoding(NSISOLatin1StringEncoding)! let latin1 = NSString(data: utf8, encoding: NSUTF8StringEncoding)! name = latin1 as String } -func doubleEncodeUTF8(inout str:String) { +func doubleEncodeUTF8(inout str: String) { let latin1 = str.dataUsingEncoding(NSUTF8StringEncoding)! let utf8 = NSString(data: latin1, encoding: NSISOLatin1StringEncoding)! str = utf8 as String diff --git a/SocketIOClientSwift/SocketIOClient.swift b/SocketIOClientSwift/SocketIOClient.swift index 0dffd87..c8ee902 100644 --- a/SocketIOClientSwift/SocketIOClient.swift +++ b/SocketIOClientSwift/SocketIOClient.swift @@ -1,6 +1,6 @@ // // SocketIOClient.swift -// Socket.IO-Swift +// Socket.IO-Client-Swift // // Created by Erik Little on 11/23/14. // @@ -25,59 +25,40 @@ import Foundation public final class SocketIOClient: NSObject, SocketEngineClient, SocketLogClient { - private var anyHandler:((SocketAnyEvent) -> Void)? - private var _closed = false - private var _connected = false - private var _connecting = false + private var anyHandler: ((SocketAnyEvent) -> Void)? private var currentReconnectAttempt = 0 private var handlers = ContiguousArray() - private var connectParams:[String: AnyObject]? - private var _secure = false - private var _reconnecting = false - private var reconnectTimer:NSTimer? + private var connectParams: [String: AnyObject]? + private var reconnectTimer: NSTimer? - let reconnectAttempts:Int! + let reconnectAttempts: Int! let logType = "SocketClient" var ackHandlers = SocketAckManager() var currentAck = -1 - var log = false - var waitingData = ContiguousArray() - var sessionDelegate:NSURLSessionDelegate? + var waitingData = [SocketPacket]() - public let socketURL:String - public let handleAckQueue = dispatch_queue_create("handleAckQueue", DISPATCH_QUEUE_SERIAL) - public let handleQueue = dispatch_queue_create("handleQueue", DISPATCH_QUEUE_SERIAL) public let emitQueue = dispatch_queue_create("emitQueue", DISPATCH_QUEUE_SERIAL) - public var closed:Bool { - return _closed - } - public var connected:Bool { - return _connected - } - public var connecting:Bool { - return _connecting - } - public var engine:SocketEngine? + public let handleQueue: dispatch_queue_t! + public let socketURL: String + + public private(set) var engine: SocketEngine? + public private(set) var secure = false + public private(set) var status = SocketIOClientStatus.NotConnected + public var nsp = "/" - public var opts:[String: AnyObject]? + public var opts: [String: AnyObject]? public var reconnects = true - public var reconnecting:Bool { - return _reconnecting - } public var reconnectWait = 10 - public var secure:Bool { - return _secure - } - public var sid:String? { + public var sid: String? { return engine?.sid } /** Create a new SocketIOClient. opts can be omitted */ - public init(var socketURL:String, opts:[String: AnyObject]? = nil) { + public init(var socketURL: String, opts: [String: AnyObject]? = nil) { if socketURL["https://"].matches().count != 0 { - self._secure = true + self.secure = true } socketURL = socketURL["http://"] ~= "" @@ -86,24 +67,15 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketLogClient self.socketURL = socketURL self.opts = opts - // Set options - if let sessionDelegate = opts?["sessionDelegate"] as? NSURLSessionDelegate { - self.sessionDelegate = sessionDelegate - } - if let connectParams = opts?["connectParams"] as? [String: AnyObject] { self.connectParams = connectParams } if let log = opts?["log"] as? Bool { - self.log = log + SocketLogger.log = log } - if var nsp = opts?["nsp"] as? String { - if nsp != "/" && nsp.hasPrefix("/") { - nsp.removeAtIndex(nsp.startIndex) - } - + if let nsp = opts?["nsp"] as? String { self.nsp = nsp } @@ -121,13 +93,15 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketLogClient self.reconnectWait = abs(reconnectWait) } + if let handleQueue = opts?["handleQueue"] as? dispatch_queue_t { + self.handleQueue = handleQueue + } else { + self.handleQueue = dispatch_get_main_queue() + } + super.init() } - public convenience init(socketURL:String, options:[String: AnyObject]?) { - self.init(socketURL: socketURL, opts: options) - } - deinit { SocketLogger.log("Client is being deinit", client: self) engine?.close(fast: true) @@ -149,15 +123,12 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketLogClient Will turn off automatic reconnects. Pass true to fast if you're closing from a background task */ - public func close(#fast:Bool) { + public func close(fast fast: Bool) { SocketLogger.log("Closing socket", client: self) reconnects = false - _connecting = false - _connected = false - _reconnecting = false + status = SocketIOClientStatus.Closed engine?.close(fast: fast) - engine = nil } /** @@ -170,42 +141,42 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketLogClient /** Connect to the server. If we aren't connected after timeoutAfter, call handler */ - public func connect(#timeoutAfter:Int, withTimeoutHandler handler:(() -> Void)?) { - if closed { - SocketLogger.log("Warning! This socket was previously closed. This might be dangerous!", client: self) - _closed = false - } else if connected { - return - } - - _connecting = true - addEngine() - engine?.open(opts: connectParams) - - if timeoutAfter == 0 { - return - } - - let time = dispatch_time(DISPATCH_TIME_NOW, Int64(timeoutAfter) * Int64(NSEC_PER_SEC)) - - dispatch_after(time, dispatch_get_main_queue()) {[weak self] in - if let this = self where !this.connected { - this._closed = true - this._connecting = false - this.engine?.close(fast: true) - - handler?() + public func connect(timeoutAfter timeoutAfter:Int, + withTimeoutHandler handler:(() -> Void)?) { + guard status != SocketIOClientStatus.Connected else { + return + } + if status == SocketIOClientStatus.Closed { + SocketLogger.log("Warning! This socket was previously closed. This might be dangerous!", client: self) + } + + status = SocketIOClientStatus.Connecting + addEngine() + engine?.open(connectParams) + + guard timeoutAfter != 0 else { + return + } + + let time = dispatch_time(DISPATCH_TIME_NOW, Int64(timeoutAfter) * Int64(NSEC_PER_SEC)) + + dispatch_after(time, dispatch_get_main_queue()) {[weak self] in + if let this = self where this.status != SocketIOClientStatus.Connected { + this.status = SocketIOClientStatus.Closed + this.engine?.close(fast: true) + + handler?() + } } - } } - private func createOnAck(event:String, items:[AnyObject]) -> OnAckCallback { + private func createOnAck(items: [AnyObject]) -> OnAckCallback { return {[weak self, ack = ++currentAck] timeout, callback in if let this = self { this.ackHandlers.addAck(ack, callback: callback) dispatch_async(this.emitQueue) {[weak this] in - this?._emit(event, items, ack: ack) + this?._emit(items, ack: ack) } if timeout != 0 { @@ -221,11 +192,7 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketLogClient func didConnect() { SocketLogger.log("Socket connected", client: self) - - _closed = false - _connected = true - _connecting = false - _reconnecting = false + status = SocketIOClientStatus.Connected currentReconnectAttempt = 0 clearReconnectTimer() @@ -234,18 +201,16 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketLogClient handleEvent("connect", data: nil, isInternalMessage: false) } - func didDisconnect(reason:String) { - if closed { + func didDisconnect(reason: String) { + guard status != SocketIOClientStatus.Closed else { return } SocketLogger.log("Disconnected: %@", client: self, args: reason) - _closed = true - _connected = false + status = SocketIOClientStatus.Closed + reconnects = false - _connecting = false - _reconnecting = false // Make sure the engine is actually dead. engine?.close(fast: true) @@ -253,7 +218,7 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketLogClient } /// error - public func didError(reason:AnyObject) { + public func didError(reason: AnyObject) { SocketLogger.err("%@", client: self, args: reason) handleEvent("error", data: reason as? [AnyObject] ?? [reason], @@ -263,33 +228,27 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketLogClient /** Same as close */ - public func disconnect(#fast:Bool) { + public func disconnect(fast fast: Bool) { close(fast: fast) } /** Send a message to the server */ - public func emit(event:String, _ items:AnyObject...) { - if !connected { - return - } - - dispatch_async(emitQueue) {[weak self] in - self?._emit(event, items) - } + public func emit(event: String, _ items: AnyObject...) { + emit(event, withItems: items) } /** Same as emit, but meant for Objective-C */ - public func emit(event:String, withItems items:[AnyObject]) { - if !connected { + public func emit(event: String, withItems items: [AnyObject]) { + guard status == SocketIOClientStatus.Connected else { return } dispatch_async(emitQueue) {[weak self] in - self?._emit(event, items) + self?._emit([event] + items) } } @@ -297,39 +256,28 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketLogClient Sends a message to the server, requesting an ack. Use the onAck method of SocketAckHandler to add an ack. */ - public func emitWithAck(event:String, _ items:AnyObject...) -> OnAckCallback { - if !connected { - return createOnAck(event, items: items) - } - - return createOnAck(event, items: items) + public func emitWithAck(event: String, _ items: AnyObject...) -> OnAckCallback { + return emitWithAck(event, withItems: items) } /** Same as emitWithAck, but for Objective-C */ - public func emitWithAck(event:String, withItems items:[AnyObject]) -> OnAckCallback { - if !connected { - return createOnAck(event, items: items) - } - - return createOnAck(event, items: items) + public func emitWithAck(event: String, withItems items: [AnyObject]) -> OnAckCallback { + return createOnAck([event] + items) } - private func _emit(event:String, _ args:[AnyObject], ack:Int? = nil) { - if !connected { + private func _emit(data: [AnyObject], ack: Int? = nil) { + guard status == SocketIOClientStatus.Connected else { return } - let packet = SocketPacket(type: nil, data: args, nsp: nsp, id: ack) - let str:String - - SocketParser.parseForEmit(packet) - str = packet.createMessageForEvent(event) + let packet = SocketPacket.packetFromEmit(data, id: ack ?? -1, nsp: nsp, ack: false) + let str = packet.packetString SocketLogger.log("Emitting: %@", client: self, args: str) - if packet.type == SocketPacket.PacketType.BINARY_EVENT { + if packet.type == SocketPacket.PacketType.BinaryEvent { engine?.send(str, withData: packet.binary) } else { engine?.send(str, withData: nil) @@ -337,18 +285,15 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketLogClient } // If the server wants to know that the client received data - func emitAck(ack:Int, withData args:[AnyObject]) { + func emitAck(ack: Int, withItems items: [AnyObject]) { dispatch_async(emitQueue) {[weak self] in - if let this = self where this.connected { - let packet = SocketPacket(type: nil, data: args, nsp: this.nsp, id: ack) - let str:String - - SocketParser.parseForEmit(packet) - str = packet.createAck() + if let this = self where this.status == SocketIOClientStatus.Connected { + let packet = SocketPacket.packetFromEmit(items, id: ack ?? -1, nsp: this.nsp, ack: true) + let str = packet.packetString SocketLogger.log("Emitting Ack: %@", client: this, args: str) - if packet.type == SocketPacket.PacketType.BINARY_ACK { + if packet.type == SocketPacket.PacketType.BinaryAck { this.engine?.send(str, withData: packet.binary) } else { this.engine?.send(str, withData: nil) @@ -358,20 +303,20 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketLogClient } } - public func engineDidClose(reason:String) { - _connected = false - _connecting = false + public func engineDidClose(reason: String) { + waitingData.removeAll() - if closed || !reconnects { + if status == SocketIOClientStatus.Closed || !reconnects { didDisconnect(reason) - } else if !reconnecting { + } else if status != SocketIOClientStatus.Reconnecting { + status = SocketIOClientStatus.Reconnecting handleEvent("reconnect", data: [reason], isInternalMessage: true) tryReconnect() } } // Called when the socket gets an ack for something it sent - func handleAck(ack:Int, data:AnyObject?) { + func handleAck(ack: Int, data: AnyObject?) { SocketLogger.log("Handling ack: %@ with data: %@", client: self, args: ack, data ?? "") @@ -382,27 +327,29 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketLogClient /** Causes an event to be handled. Only use if you know what you're doing. */ - public func handleEvent(event:String, data:[AnyObject]?, isInternalMessage:Bool = false, - wantsAck ack:Int? = nil) { - // println("Should do event: \(event) with data: \(data)") - if !connected && !isInternalMessage { + public func handleEvent(event: String, data: [AnyObject]?, isInternalMessage: Bool, + wantsAck ack: Int? = nil) { + guard status == SocketIOClientStatus.Connected || isInternalMessage else { return } + // println("Should do event: \(event) with data: \(data)") SocketLogger.log("Handling event: %@ with data: %@", client: self, args: event, data ?? "") if anyHandler != nil { - dispatch_async(dispatch_get_main_queue()) {[weak self] in + dispatch_async(handleQueue) {[weak self] in self?.anyHandler?(SocketAnyEvent(event: event, items: data)) } } - for handler in handlers { - if handler.event == event { - if ack != nil { - handler.executeCallback(data, withAck: ack!, withSocket: self) - } else { + for handler in handlers where handler.event == event { + if let ack = ack { + dispatch_async(handleQueue) {[weak self] in + handler.executeCallback(data, withAck: ack, withSocket: self) + } + } else { + dispatch_async(handleQueue) { handler.executeCallback(data) } } @@ -414,7 +361,7 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketLogClient */ public func leaveNamespace() { if nsp != "/" { - engine?.send("1/\(nsp)", withData: nil) + engine?.send("1\(nsp)", withData: nil) nsp = "/" } } @@ -426,41 +373,59 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketLogClient SocketLogger.log("Joining namespace", client: self) if nsp != "/" { - engine?.send("0/\(nsp)", withData: nil) + engine?.send("0\(nsp)", withData: nil) } } + /** + Joins namespace / + */ + public func joinNamespace(namespace: String) { + self.nsp = namespace + joinNamespace() + } + /** Removes handler(s) */ - public func off(event:String) { + public func off(event: String) { SocketLogger.log("Removing handler for event: %@", client: self, args: event) - handlers = handlers.filter {$0.event == event ? false : true} + handlers = ContiguousArray(handlers.filter {!($0.event == event)}) } /** Adds a handler for an event. */ - public func on(event:String, callback:NormalCallback) { + public func on(event: String, callback: NormalCallback) { SocketLogger.log("Adding handler for event: %@", client: self, args: event) let handler = SocketEventHandler(event: event, callback: callback) handlers.append(handler) } - - /** - Removes all handlers. - Can be used after disconnecting to break any potential remaining retain cycles. - */ - public func removeAllHandlers() { - handlers.removeAll(keepCapacity: false) - } + + /** + Adds a handler for an event. + */ + public func onObjectiveC(event: String, callback: NormalCallbackObjectiveC) { + SocketLogger.log("Adding handler for event: %@", client: self, args: event) + + let handler = SocketEventHandler(event: event, callback: callback) + handlers.append(handler) + } + + /** + Removes all handlers. + Can be used after disconnecting to break any potential remaining retain cycles. + */ + public func removeAllHandlers() { + handlers.removeAll(keepCapacity: false) + } /** Adds a handler that will be called on every event. */ - public func onAny(handler:(SocketAnyEvent) -> Void) { + public func onAny(handler: (SocketAnyEvent) -> Void) { anyHandler = handler } @@ -471,50 +436,58 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketLogClient connect() } - public func parseSocketMessage(msg:String) { - SocketParser.parseSocketMessage(msg, socket: self) + public func parseSocketMessage(msg: String) { + dispatch_async(handleQueue) {[weak self] in + if let this = self { + SocketParser.parseSocketMessage(msg, socket: this) + } + } } - public func parseBinaryData(data:NSData) { - SocketParser.parseBinaryData(data, socket: self) + public func parseBinaryData(data: NSData) { + dispatch_async(handleQueue) {[weak self] in + if let this = self { + SocketParser.parseBinaryData(data, socket: this) + } + } } /** - Trieds to reconnect to the server. + Tries to reconnect to the server. */ public func reconnect() { - _connected = false - _connecting = false - _reconnecting = false - engine?.stopPolling() tryReconnect() } - // We lost connection and should attempt to reestablish - @objc private func tryReconnect() { + private func tryReconnect() { + if reconnectTimer == nil { + SocketLogger.log("Starting reconnect", client: self) + + status = SocketIOClientStatus.Reconnecting + + dispatch_async(dispatch_get_main_queue()) {[weak self] in + if let this = self { + this.reconnectTimer = NSTimer.scheduledTimerWithTimeInterval(Double(this.reconnectWait), + target: this, selector: "_tryReconnect", userInfo: nil, repeats: true) + } + } + } + } + + @objc private func _tryReconnect() { + if status == SocketIOClientStatus.Connected { + clearReconnectTimer() + + return + } + + if reconnectAttempts != -1 && currentReconnectAttempt + 1 > reconnectAttempts || !reconnects { clearReconnectTimer() didDisconnect("Reconnect Failed") return - } else if connected { - _connecting = false - _reconnecting = false - return - } - - if reconnectTimer == nil { - SocketLogger.log("Starting reconnect", client: self) - - _reconnecting = true - - dispatch_async(dispatch_get_main_queue()) {[weak self] in - if let this = self { - this.reconnectTimer = NSTimer.scheduledTimerWithTimeInterval(Double(this.reconnectWait), - target: this, selector: "tryReconnect", userInfo: nil, repeats: true) - } - } } SocketLogger.log("Trying to reconnect", client: self) diff --git a/SocketIOClientSwift/SocketIOClientStatus.swift b/SocketIOClientSwift/SocketIOClientStatus.swift new file mode 100644 index 0000000..1817f51 --- /dev/null +++ b/SocketIOClientSwift/SocketIOClientStatus.swift @@ -0,0 +1,51 @@ +// +// SocketIOClientStatus.swift +// Socket.IO-Client-Swift +// +// Created by Erik Little on 8/14/15. +// +// 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. + +import Foundation + +@objc public enum SocketIOClientStatus: Int, CustomStringConvertible { + public var description: String { + let des: String + + switch rawValue { + case 0: + des = "Not Connected" + case 1: + des = "Closed" + case 2: + des = "Connecting" + case 3: + des = "Connected" + case 4: + des = "Reconnecting" + default: + des = "Unknown State" + } + + return des + } + + case NotConnected, Closed, Connecting, Connected, Reconnecting + +} \ No newline at end of file diff --git a/SocketIOClientSwift/SocketLogger.swift b/SocketIOClientSwift/SocketLogger.swift index 16a4378..5071c02 100644 --- a/SocketIOClientSwift/SocketLogger.swift +++ b/SocketIOClientSwift/SocketLogger.swift @@ -1,6 +1,6 @@ // // SocketLogger.swift -// SocketIO-Swift +// Socket.IO-Client-Swift // // Created by Erik Little on 4/11/15. // @@ -24,48 +24,38 @@ import Foundation -private let MESSAGE_LENGTH_MAX = 10000 - protocol SocketLogClient { - var log:Bool {get set} - var logType:String {get} + var logType: String {get} } final class SocketLogger { private static let printQueue = dispatch_queue_create("printQueue", DISPATCH_QUEUE_SERIAL) + static var log = false - private static func shorten(item:AnyObject) -> CVarArgType { - var str = toString(item) - - if count(str) > MESSAGE_LENGTH_MAX { - let endIndex = advance(str.startIndex, MESSAGE_LENGTH_MAX) - - str = str.substringToIndex(endIndex) - } - - return str + private static func toCVArgType(item: AnyObject) -> CVarArgType { + return String(item) } - static func log(message:String, client:SocketLogClient, altType:String? = nil, args:AnyObject...) { - if !client.log { + static func log(message: String, client: SocketLogClient, altType: String? = nil, args: AnyObject...) { + if !log { return } dispatch_async(printQueue) {[type = client.logType] in - let newArgs = args.map(SocketLogger.shorten) + let newArgs = args.map(SocketLogger.toCVArgType) let replaced = String(format: message, arguments: newArgs) NSLog("%@: %@", altType ?? type, replaced) } } - static func err(message:String, client:SocketLogClient, altType:String? = nil, args:AnyObject...) { - if !client.log { + static func err(message: String, client: SocketLogClient, altType: String? = nil, args: AnyObject...) { + if !log { return } dispatch_async(printQueue) {[type = client.logType] in - let newArgs = args.map(SocketLogger.shorten) + let newArgs = args.map(SocketLogger.toCVArgType) let replaced = String(format: message, arguments: newArgs) NSLog("ERROR %@: %@", altType ?? type, replaced) diff --git a/SocketIOClientSwift/SocketPacket.swift b/SocketIOClientSwift/SocketPacket.swift index 5dcfd36..e25bd9d 100644 --- a/SocketIOClientSwift/SocketPacket.swift +++ b/SocketIOClientSwift/SocketPacket.swift @@ -1,6 +1,6 @@ // // SocketPacket.swift -// Socket.IO-Swift +// Socket.IO-Client-Swift // // Created by Erik Little on 1/18/15. // @@ -24,38 +24,19 @@ import Foundation -final class SocketPacket: Printable { - var binary = ContiguousArray() - var currentPlace = 0 - var data:[AnyObject]? - var description:String { - var better = "SocketPacket {type: ~~0; data: ~~1; " + - "id: ~~2; placeholders: ~~3;}" - - better = better["~~0"] ~= (type != nil ? String(type!.rawValue) : "nil") - better = better["~~1"] ~= (data != nil ? "\(data!)" : "nil") - better = better["~~2"] ~= (id != nil ? String(id!) : "nil") - better = better["~~3"] ~= (placeholders != nil ? String(placeholders!) : "nil") - - return better - } - var id:Int? - var justAck = false - var nsp = "" - var placeholders:Int? - var type:PacketType? +struct SocketPacket { + private var currentPlace = 0 + private let placeholders: Int + + let nsp: String + let id: Int + let type: PacketType - enum PacketType:Int { - case CONNECT = 0 - case DISCONNECT = 1 - case EVENT = 2 - case ACK = 3 - case ERROR = 4 - case BINARY_EVENT = 5 - case BINARY_ACK = 6 + enum PacketType: Int { + case Connect, Disconnect, Event, Ack, Error, BinaryEvent, BinaryAck - init?(str:String) { - if let int = str.toInt(), raw = PacketType(rawValue: int) { + init?(str: String) { + if let int = Int(str), raw = PacketType(rawValue: int) { self = raw } else { return nil @@ -63,20 +44,54 @@ final class SocketPacket: Printable { } } - init(type:PacketType?, data:[AnyObject]? = nil, nsp:String = "", - placeholders:Int? = nil, id:Int? = nil) { - self.type = type - self.data = data - self.nsp = nsp - self.placeholders = placeholders - self.id = id + var args: [AnyObject]? { + var arr = data + + if data.count == 0 { + return nil + } else { + if type == PacketType.Event || type == PacketType.BinaryEvent { + arr.removeAtIndex(0) + return arr + } else { + return arr + } + } } - func getEvent() -> String { - return data?.removeAtIndex(0) as! String + var binary: [NSData] + var data: [AnyObject] + var description: String { + var better = "SocketPacket {type: ~~0; data: ~~1; " + + "id: ~~2; placeholders: ~~3;}" + + better = better["~~0"] ~= String(type.rawValue) + better = better["~~1"] ~= String(data) + better = better["~~2"] ~= String(id) + better = better["~~3"] ~= String(placeholders) + + return better } - func addData(data:NSData) -> Bool { + var event: String { + return data[0] as! String + } + + var packetString: String { + return createPacketString() + } + + init(type: SocketPacket.PacketType, data: [AnyObject] = [AnyObject](), id: Int = -1, + nsp: String, placeholders: Int = 0, binary: [NSData] = [NSData]()) { + self.data = data + self.id = id + self.nsp = nsp + self.type = type + self.placeholders = placeholders + self.binary = binary + } + + mutating func addData(data: NSData) -> Bool { if placeholders == currentPlace { return true } @@ -92,86 +107,22 @@ final class SocketPacket: Printable { } } - func createMessageForEvent(event:String) -> String { - let message:String - - if binary.count == 0 { - type = PacketType.EVENT - - if nsp == "/" { - if id == nil { - message = "2[\"\(event)\"" - } else { - message = "2\(id!)[\"\(event)\"" - } - } else { - if id == nil { - message = "2/\(nsp),[\"\(event)\"" - } else { - message = "2/\(nsp),\(id!)[\"\(event)\"" - } - } - } else { - type = PacketType.BINARY_EVENT - - if nsp == "/" { - if id == nil { - message = "5\(binary.count)-[\"\(event)\"" - } else { - message = "5\(binary.count)-\(id!)[\"\(event)\"" - } - } else { - if id == nil { - message = "5\(binary.count)-/\(nsp),[\"\(event)\"" - } else { - message = "5\(binary.count)-/\(nsp),\(id!)[\"\(event)\"" - } - } - } - - return completeMessage(message) - } - - func createAck() -> String { - var msg:String - - if binary.count == 0 { - type = PacketType.ACK - - if nsp == "/" { - msg = "3\(id!)[" - } else { - msg = "3/\(nsp),\(id!)[" - } - } else { - type = PacketType.BINARY_ACK - - if nsp == "/" { - msg = "6\(binary.count)-\(id!)[" - } else { - msg = "6\(binary.count)-/\(nsp),\(id!)[" - } - } - - return completeMessage(msg, ack: true) - } - - private func completeMessage(var message:String, ack:Bool = false) -> String { - var err:NSError? - - if data == nil || data!.count == 0 { + private func completeMessage(var message: String, ack: Bool) -> String { + if data.count == 0 { return message + "]" - } else if !ack { - message += "," } - for arg in data! { + for arg in data { if arg is NSDictionary || arg is [AnyObject] { - let jsonSend = NSJSONSerialization.dataWithJSONObject(arg, - options: NSJSONWritingOptions(0), error: &err) - let jsonString = NSString(data: jsonSend!, encoding: NSUTF8StringEncoding) - - message += jsonString! as String + "," + do { + let jsonSend = try NSJSONSerialization.dataWithJSONObject(arg, + options: NSJSONWritingOptions(rawValue: 0)) + let jsonString = NSString(data: jsonSend, encoding: NSUTF8StringEncoding) + + message += jsonString! as String + "," + } catch { + print("Error creating JSON object in SocketPacket.completeMessage") + } } else if var str = arg as? String { str = str["\n"] ~= "\\\\n" str = str["\r"] ~= "\\\\r" @@ -191,29 +142,98 @@ final class SocketPacket: Printable { return message + "]" } - func fillInPlaceholders() { - var newArr = NSMutableArray(array: data!) + private func createAck() -> String { + let msg: String - for i in 0.. String { + let message: String + + if type == PacketType.Event { + if nsp == "/" { + if id == -1 { + message = "2[" + } else { + message = "2\(id)[" + } + } else { + if id == -1 { + message = "2\(nsp),[" + } else { + message = "2\(nsp),\(id)[" + } + } + } else { + if nsp == "/" { + if id == -1 { + message = "5\(binary.count)-[" + } else { + message = "5\(binary.count)-\(id)[" + } + } else { + if id == -1 { + message = "5\(binary.count)-\(nsp),[" + } else { + message = "5\(binary.count)-\(nsp),\(id)[" + } + } + } + + return completeMessage(message, ack: false) + } + + private func createPacketString() -> String { + let str: String + + if type == PacketType.Event || type == PacketType.BinaryEvent { + str = createMessageForEvent() + } else { + str = createAck() + } + + return str + } + + mutating func fillInPlaceholders() { + let newArr = NSMutableArray(array: data) + + for i in 0.. AnyObject { + private mutating func _fillInPlaceholders(data: AnyObject) -> AnyObject { if let str = data as? String { if let num = str["~~(\\d)"].groups() { - return binary[num[1].toInt()!] + return binary[Int(num[1])!] } else { return str } } else if let dict = data as? NSDictionary { - var newDict = NSMutableDictionary(dictionary: dict) + let newDict = NSMutableDictionary(dictionary: dict) for (key, value) in dict { newDict[key as! NSCopying] = _fillInPlaceholders(value) @@ -221,7 +241,7 @@ final class SocketPacket: Printable { return newDict } else if let arr = data as? NSArray { - var newArr = NSMutableArray(array: arr) + let newArr = NSMutableArray(array: arr) for i in 0.. PacketType { + switch binCount { + case 0 where !ack: + return PacketType.Event + case 0 where ack: + return PacketType.Ack + case _ where !ack: + return PacketType.BinaryEvent + case _ where ack: + return PacketType.BinaryAck + default: + return PacketType.Error + } + } + + static func packetFromEmit(items: [AnyObject], id: Int, nsp: String, ack: Bool) -> SocketPacket { + let (parsedData, binary) = deconstructData(items) + let packet = SocketPacket(type: findType(binary.count, ack: ack), data: parsedData, + id: id, nsp: nsp, placeholders: -1, binary: binary) + + return packet + } +} + +private extension SocketPacket { + static func shred(data: AnyObject, inout binary: [NSData]) -> AnyObject { + if let bin = data as? NSData { + let placeholder = ["_placeholder" :true, "num": binary.count] + + binary.append(bin) + + return placeholder + } else if let arr = data as? NSArray { + let newArr = NSMutableArray(array: arr) + + for i in 0.. ([AnyObject], [NSData]) { + var binary = [NSData]() + + for i in 0.. Bool { + return nsp == socket.nsp + } - // Translation of socket.io-parser#deconstructPacket - private final class PacketShredder { - var buf = ContiguousArray() - - func shred(data:AnyObject) -> AnyObject { - if let bin = data as? NSData { - let placeholder = ["_placeholder" :true, "num": buf.count] - - buf.append(bin) - - return placeholder - } else if let arr = data as? NSArray { - var newArr = NSMutableArray(array: arr) - - for i in 0.. SocketPacket? { - let arr = Array(str) + static func parseString(str: String) -> SocketPacket? { + let arr = Array(str.characters) let type = String(arr[0]) if arr.count == 1 { - return SocketPacket(type: SocketPacket.PacketType(str: type)) + return SocketPacket(type: SocketPacket.PacketType(str: type)!, nsp: "/") } - var id = nil as Int? - var nsp = "" + var id: Int? + var nsp:String? var i = 0 var placeholders = -1 @@ -103,7 +94,7 @@ class SocketParser { } } - if let holders = buf.toInt() where arr[i] == "-" { + if let holders = Int(buf) where arr[i] == "-" { placeholders = holders } else { NSLog("Error parsing \(str)") @@ -112,6 +103,8 @@ class SocketParser { } if arr[i + 1] == "/" { + nsp = "" + while ++i < arr.count { let c = arr[i] @@ -119,49 +112,56 @@ class SocketParser { break } - nsp += String(c) + nsp! += String(c) } } if i + 1 >= arr.count { - return SocketPacket(type: SocketPacket.PacketType(str: type), - nsp: nsp, placeholders: placeholders, id: id) + return SocketPacket(type: SocketPacket.PacketType(str: type)!, id: id ?? -1, + nsp: nsp ?? "/", placeholders: placeholders) } let next = String(arr[i + 1]) - if next.toInt() != nil { + if Int(next) != nil { var c = "" while ++i < arr.count { - if let int = String(arr[i]).toInt() { - c += String(arr[i]) + if let int = Int(String(arr[i])) { + c += String(int) } else { --i break } } - id = c.toInt() + id = Int(c) } if ++i < arr.count { - let d = str[advance(str.startIndex, i)...advance(str.startIndex, count(str)-1)] + let d = str[str.startIndex.advancedBy(i)...str.startIndex.advancedBy(str.characters.count-1)] let noPlaceholders = d["(\\{\"_placeholder\":true,\"num\":(\\d*)\\})"] ~= "\"~~$2\"" let data = SocketParser.parseData(noPlaceholders) as? [AnyObject] ?? [noPlaceholders] - return SocketPacket(type: SocketPacket.PacketType(str: type), data: data, - nsp: nsp, placeholders: placeholders, id: id) + return SocketPacket(type: SocketPacket.PacketType(str: type)!, data: data, id: id ?? -1, + nsp: nsp ?? "/", placeholders: placeholders) } return nil } // Parses data for events - static func parseData(data:String) -> AnyObject? { - var err:NSError? + static func parseData(data: String) -> AnyObject? { + var err: NSError? let stringData = data.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) - let parsed:AnyObject? = NSJSONSerialization.JSONObjectWithData(stringData!, - options: NSJSONReadingOptions.MutableContainers, error: &err) + let parsed: AnyObject? + + do { + parsed = try NSJSONSerialization.JSONObjectWithData(stringData!, + options: NSJSONReadingOptions.MutableContainers) + } catch let error as NSError { + err = error + parsed = nil + } if err != nil { // println(err) @@ -171,23 +171,15 @@ class SocketParser { return parsed } - static func parseForEmit(packet:SocketPacket) { - shredder.deconstructPacket(packet) - } - // Parses messages recieved - static func parseSocketMessage(stringMessage:String, socket:SocketIOClient) { + static func parseSocketMessage(stringMessage: String, socket: SocketIOClient) { if stringMessage == "" { return } - func checkNSP(nsp:String) -> Bool { - return nsp == "" && socket.nsp != "/" - } - SocketLogger.log("Parsing %@", client: socket, altType: "SocketParser", args: stringMessage) - let p:SocketPacket + let p: SocketPacket if let pack = parseString(stringMessage) { p = pack @@ -196,50 +188,27 @@ class SocketParser { return } - SocketLogger.log("Decoded packet as: %@", client: socket, altType: "SocketParser", args: p) + SocketLogger.log("Decoded packet as: %@", client: socket, altType: "SocketParser", args: p.description) - if p.type == SocketPacket.PacketType.EVENT { - if checkNSP(p.nsp) { - return - } - - socket.handleEvent(p.getEvent(), data: p.data, - isInternalMessage: false, wantsAck: p.id) - } else if p.type == SocketPacket.PacketType.ACK { - if checkNSP(p.nsp) { - return - } - - socket.handleAck(p.id!, data: p.data) - } else if p.type == SocketPacket.PacketType.BINARY_EVENT { - if checkNSP(p.nsp) { - return - } - - socket.waitingData.append(p) - } else if p.type == SocketPacket.PacketType.BINARY_ACK { - if checkNSP(p.nsp) { - return - } - - p.justAck = true - socket.waitingData.append(p) - } else if p.type == SocketPacket.PacketType.CONNECT { - if p.nsp == "" && socket.nsp != "/" { - socket.joinNamespace() - } else if p.nsp != "" && socket.nsp == "/" { - socket.didConnect() - } else { - socket.didConnect() - } - } else if p.type == SocketPacket.PacketType.DISCONNECT { + switch p.type { + case SocketPacket.PacketType.Event: + handleEvent(p, socket: socket) + case SocketPacket.PacketType.Ack: + handleAck(p, socket: socket) + case SocketPacket.PacketType.BinaryEvent: + handleBinaryEvent(p, socket: socket) + case SocketPacket.PacketType.BinaryAck: + handleBinaryAck(p, socket: socket) + case SocketPacket.PacketType.Connect: + handleConnect(p, socket: socket) + case SocketPacket.PacketType.Disconnect: socket.didDisconnect("Got Disconnect") - } else if p.type == SocketPacket.PacketType.ERROR { - socket.didError(p.data == nil ? "Error" : p.data!) + case SocketPacket.PacketType.Error: + socket.didError("Error: \(p.data)") } } - static func parseBinaryData(data:NSData, socket:SocketIOClient) { + static func parseBinaryData(data: NSData, socket: SocketIOClient) { if socket.waitingData.count == 0 { SocketLogger.err("Got data when not remaking packet", client: socket, altType: "SocketParser") return @@ -251,14 +220,14 @@ class SocketParser { return } - let packet = socket.waitingData.removeAtIndex(0) + var packet = socket.waitingData.removeAtIndex(0) packet.fillInPlaceholders() - if !packet.justAck { - socket.handleEvent(packet.getEvent(), data: packet.data, - wantsAck: packet.id) + if packet.type != SocketPacket.PacketType.BinaryAck { + socket.handleEvent(packet.event, data: packet.args, + isInternalMessage: false, wantsAck: packet.id) } else { - socket.handleAck(packet.id!, data: packet.data) + socket.handleAck(packet.id, data: packet.args) } } } diff --git a/SocketIOClientSwift/SocketTypes.swift b/SocketIOClientSwift/SocketTypes.swift index 8562024..e27bbe3 100644 --- a/SocketIOClientSwift/SocketTypes.swift +++ b/SocketIOClientSwift/SocketTypes.swift @@ -1,6 +1,6 @@ // // SocketTypes.swift -// SocketIO-Swift +// Socket.IO-Client-Swift // // Created by Erik Little on 4/8/15. // @@ -24,11 +24,10 @@ import Foundation -// @objc_block is undocumented, but is used because Swift assumes that all -// Objective-C blocks are copied, but Objective-C assumes that Swift will copy it. -// And the way things are done here, the bridging fails to copy the block in -// SocketAckMap#addAck -public typealias AckCallback = @objc_block (NSArray?) -> Void +public typealias AckCallback = (NSArray?) -> Void public typealias AckEmitter = (AnyObject...) -> Void +public typealias AckEmitterObjectiveC = (NSArray) -> Void public typealias NormalCallback = (NSArray?, AckEmitter?) -> Void -public typealias OnAckCallback = (timeout:UInt64, callback:AckCallback) -> Void +public typealias NormalCallbackObjectiveC = (NSArray?, AckEmitterObjectiveC?) -> Void +public typealias OnAckCallback = (timeoutAfter:UInt64, callback:AckCallback) -> Void + diff --git a/SocketIOClientSwift/SwiftRegex.swift b/SocketIOClientSwift/SwiftRegex.swift index 2bbb363..fb306aa 100644 --- a/SocketIOClientSwift/SwiftRegex.swift +++ b/SocketIOClientSwift/SwiftRegex.swift @@ -18,34 +18,34 @@ var swiftRegexCache = [String: NSRegularExpression]() public class SwiftRegex: NSObject, BooleanType { var target:String var regex: NSRegularExpression - - init(target:String, pattern:String, options:NSRegularExpressionOptions = nil) { + + init(target:String, pattern:String, options:NSRegularExpressionOptions?) { self.target = target if let regex = swiftRegexCache[pattern] { self.regex = regex } else { - var error: NSError? - if let regex = NSRegularExpression(pattern: pattern, options:options, error:&error) { + do { + let regex = try NSRegularExpression(pattern: pattern, options: + NSRegularExpressionOptions.DotMatchesLineSeparators) swiftRegexCache[pattern] = regex self.regex = regex - } - else { - SwiftRegex.failure("Error in pattern: \(pattern) - \(error)") + } catch let error1 as NSError { + SwiftRegex.failure("Error in pattern: \(pattern) - \(error1)") self.regex = NSRegularExpression() } } super.init() } - + class func failure(message: String) { - println("SwiftRegex: "+message) + print("SwiftRegex: "+message) //assert(false,"SwiftRegex: failed") } - + final var targetRange: NSRange { - return NSRange(location: 0,length: count(target.utf16)) + return NSRange(location: 0,length: target.utf16.count) } - + final func substring(range: NSRange) -> String? { if ( range.location != NSNotFound ) { return (target as NSString).substringWithRange(range) @@ -53,23 +53,24 @@ public class SwiftRegex: NSObject, BooleanType { return nil } } - - public func doesMatch(options: NSMatchingOptions = nil) -> Bool { - return range(options: options).location != NSNotFound + + public func doesMatch(options: NSMatchingOptions!) -> Bool { + return range(options).location != NSNotFound } - - public func range(options: NSMatchingOptions = nil) -> NSRange { - return regex.rangeOfFirstMatchInString(target as String, options: nil, range: targetRange) + + public func range(options: NSMatchingOptions) -> NSRange { + return regex.rangeOfFirstMatchInString(target as String, options: [], range: targetRange) } - - public func match(options: NSMatchingOptions = nil) -> String? { - return substring(range(options: options)) + + public func match(options: NSMatchingOptions) -> String? { + return substring(range(options)) } - - public func groups(options: NSMatchingOptions = nil) -> [String]? { - return groupsForMatch(regex.firstMatchInString(target as String, options: options, range: targetRange)) + + public func groups() -> [String]? { + return groupsForMatch(regex.firstMatchInString(target as String, options: + NSMatchingOptions.WithoutAnchoringBounds, range: targetRange)) } - + func groupsForMatch(match: NSTextCheckingResult!) -> [String]? { if match != nil { var groups = [String]() @@ -85,80 +86,75 @@ public class SwiftRegex: NSObject, BooleanType { return nil } } - + public subscript(groupno: Int) -> String? { get { return groups()?[groupno] } - + set(newValue) { if newValue == nil { return } - - for match in matchResults()!.reverse() { + + for match in Array(matchResults().reverse()) { let replacement = regex.replacementStringForResult(match, inString: target as String, offset: 0, template: newValue!) let mut = NSMutableString(string: target) mut.replaceCharactersInRange(match.rangeAtIndex(groupno), withString: replacement) - + target = mut as String } } } - - func matchResults(options: NSMatchingOptions = nil) -> [NSTextCheckingResult]? { - let matches = regex.matchesInString(target as String, options: options, range: targetRange) - as? [NSTextCheckingResult] - - if matches != nil { - return matches! - } else { - return nil - } + + func matchResults() -> [NSTextCheckingResult] { + let matches = regex.matchesInString(target as String, options: + NSMatchingOptions.WithoutAnchoringBounds, range: targetRange) + as [NSTextCheckingResult] + + return matches } - - public func ranges(options: NSMatchingOptions = nil) -> [NSRange] { - return matchResults(options: options)!.map { $0.range } + + public func ranges() -> [NSRange] { + return matchResults().map { $0.range } } - - public func matches(options: NSMatchingOptions = nil) -> [String] { - return matchResults(options: options)!.map( { self.substring($0.range)!}) + + public func matches() -> [String] { + return matchResults().map( { self.substring($0.range)!}) } - - public func allGroups(options: NSMatchingOptions = nil) -> [[String]?] { - return matchResults(options: options)!.map {self.groupsForMatch($0)} + + public func allGroups() -> [[String]?] { + return matchResults().map {self.groupsForMatch($0)} } - - public func dictionary(options: NSMatchingOptions = nil) -> Dictionary { + + public func dictionary(options: NSMatchingOptions!) -> Dictionary { var out = Dictionary() - for match in matchResults(options: options)! { + for match in matchResults() { out[substring(match.rangeAtIndex(1))!] = substring(match.rangeAtIndex(2))! } return out } - + func substituteMatches(substitution: ((NSTextCheckingResult, UnsafeMutablePointer) -> String), - options:NSMatchingOptions = nil) -> String { + options:NSMatchingOptions) -> String { let out = NSMutableString() var pos = 0 - - regex.enumerateMatchesInString(target as String, options: options, range: targetRange ) { - (match: NSTextCheckingResult!, flags: NSMatchingFlags, stop: UnsafeMutablePointer) in - - let matchRange = match.range + + regex.enumerateMatchesInString(target as String, options: options, range: targetRange ) {match, flags, stop in + let matchRange = match!.range out.appendString( self.substring(NSRange(location:pos, length:matchRange.location-pos))!) - out.appendString( substitution(match, stop) ) + out.appendString( substitution(match!, stop) ) pos = matchRange.location + matchRange.length } - + out.appendString(substring(NSRange(location:pos, length:targetRange.length-pos))!) - + return out as String } - + public var boolValue: Bool { - return doesMatch() + return doesMatch(nil) } } @@ -170,7 +166,7 @@ extension String { extension String { public subscript(pattern: String) -> SwiftRegex { - return SwiftRegex(target: self, pattern: pattern) + return SwiftRegex(target: self, pattern: pattern, options: nil) } } @@ -178,20 +174,20 @@ public func ~= (left: SwiftRegex, right: String) -> String { return left.substituteMatches({match, stop in return left.regex.replacementStringForResult( match, inString: left.target as String, offset: 0, template: right ) - }, options: nil) + }, options: []) } public func ~= (left: SwiftRegex, right: [String]) -> String { var matchNumber = 0 return left.substituteMatches({match, stop -> String in - + if ++matchNumber == right.count { stop.memory = true } - + return left.regex.replacementStringForResult( match, inString: left.target as String, offset: 0, template: right[matchNumber-1] ) - }, options: nil) + }, options: []) } public func ~= (left: SwiftRegex, right: (String) -> String) -> String { @@ -199,11 +195,11 @@ public func ~= (left: SwiftRegex, right: (String) -> String) -> String { return left.substituteMatches( {match, stop -> String in right(left.substring(match.range)!) - }, options: nil) + }, options: []) } public func ~= (left: SwiftRegex, right: ([String]?) -> String) -> String { return left.substituteMatches({match, stop -> String in return right(left.groupsForMatch(match)) - }, options: nil) + }, options: []) } diff --git a/SocketIOClientSwift/WebSocket.swift b/SocketIOClientSwift/WebSocket.swift index 810f1b4..04192a4 100644 --- a/SocketIOClientSwift/WebSocket.swift +++ b/SocketIOClientSwift/WebSocket.swift @@ -93,7 +93,7 @@ public class WebSocket : NSObject, NSStreamDelegate { public var headers = Dictionary() public var voipEnabled = false public var selfSignedSSL = false - public var security: Security? + private var security: Security? public var isConnected :Bool { return connected } @@ -168,7 +168,6 @@ public class WebSocket : NSObject, NSStreamDelegate { //private method that starts the connection private func createHTTPRequest() { - let str: NSString = url.absoluteString! let urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, "GET", url, kCFHTTPVersion1_1).takeRetainedValue() @@ -184,24 +183,25 @@ public class WebSocket : NSObject, NSStreamDelegate { if self.cookies != nil { let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(self.cookies!) for (key, value) in headers { - self.addHeader(urlRequest, key: key as! String, val: value as! String) + self.addHeader(urlRequest, key: key as String, val: value as String) } } self.addHeader(urlRequest, key: headerWSUpgradeName, val: headerWSUpgradeValue) self.addHeader(urlRequest, key: headerWSConnectionName, val: headerWSConnectionValue) if let protocols = optionalProtocols { - self.addHeader(urlRequest, key: headerWSProtocolName, val: ",".join(protocols)) + self.addHeader(urlRequest, key: headerWSProtocolName, val: protocols.joinWithSeparator(",")) } self.addHeader(urlRequest, key: headerWSVersionName, val: headerWSVersionValue) self.addHeader(urlRequest, key: headerWSKeyName, val: self.generateWebSocketKey()) - self.addHeader(urlRequest, key: headerOriginName, val: url.absoluteString!) + self.addHeader(urlRequest, key: headerOriginName, val: url.absoluteString) self.addHeader(urlRequest, key: headerWSHostName, val: "\(url.host!):\(port!)") for (key,value) in headers { self.addHeader(urlRequest, key: key, val: value) } - let serializedRequest: NSData = CFHTTPMessageCopySerializedMessage(urlRequest).takeRetainedValue() + + let serializedRequest: NSData = CFHTTPMessageCopySerializedMessage(urlRequest)!.takeRetainedValue() self.initStreamsWithData(serializedRequest, Int(port!)) } //Add a header to the CFHTTPMessage by using the NSString bridges to CFString @@ -220,8 +220,8 @@ public class WebSocket : NSObject, NSStreamDelegate { let uni = UnicodeScalar(UInt32(97 + arc4random_uniform(25))) key += "\(Character(uni))" } - var data = key.dataUsingEncoding(NSUTF8StringEncoding) - var baseKey = data?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(0)) + let data = key.dataUsingEncoding(NSUTF8StringEncoding) + let baseKey = data?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0)) return baseKey! } //Start the stream connection and write the data to the output stream @@ -250,8 +250,8 @@ public class WebSocket : NSObject, NSStreamDelegate { } if self.selfSignedSSL { let settings: Dictionary = [kCFStreamSSLValidatesCertificateChain: NSNumber(bool:false), kCFStreamSSLPeerName: kCFNull] - inputStream!.setProperty(settings, forKey: kCFStreamPropertySSLSettings as! String) - outputStream!.setProperty(settings, forKey: kCFStreamPropertySSLSettings as! String) + inputStream!.setProperty(settings, forKey: kCFStreamPropertySSLSettings as String) + outputStream!.setProperty(settings, forKey: kCFStreamPropertySSLSettings as String) } isRunLoop = true inputStream!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) @@ -261,16 +261,16 @@ public class WebSocket : NSObject, NSStreamDelegate { let bytes = UnsafePointer(data.bytes) outputStream!.write(bytes, maxLength: data.length) while(isRunLoop) { - NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate.distantFuture() as! NSDate) + NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate.distantFuture() as NSDate) } } //delegate for the stream methods. Processes incoming bytes public func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) { if let sec = security where !certValidated && (eventCode == .HasBytesAvailable || eventCode == .HasSpaceAvailable) { - var possibleTrust: AnyObject? = aStream.propertyForKey(kCFStreamPropertySSLPeerTrust as! String) + let possibleTrust: AnyObject? = aStream.propertyForKey(kCFStreamPropertySSLPeerTrust as String) if let trust: AnyObject = possibleTrust { - var domain: AnyObject? = aStream.propertyForKey(kCFStreamSSLPeerName as! String) + let domain: AnyObject? = aStream.propertyForKey(kCFStreamSSLPeerName as String) if sec.isValid(trust as! SecTrustRef, domain: domain as! String?) { certValidated = true } else { @@ -314,7 +314,7 @@ public class WebSocket : NSObject, NSStreamDelegate { ///handles the incoming bytes and sending them to the proper processing method private func processInputStream() { let buf = NSMutableData(capacity: BUFFER_MAX) - var buffer = UnsafeMutablePointer(buf!.bytes) + let buffer = UnsafeMutablePointer(buf!.bytes) let length = inputStream!.read(buffer, maxLength: BUFFER_MAX) if length > 0 { if !connected { @@ -340,7 +340,7 @@ public class WebSocket : NSObject, NSStreamDelegate { let data = inputQueue[0] var work = data if (fragBuffer != nil) { - var combine = NSMutableData(data: fragBuffer!) + let combine = NSMutableData(data: fragBuffer!) combine.appendData(data) work = combine fragBuffer = nil @@ -388,14 +388,14 @@ public class WebSocket : NSObject, NSStreamDelegate { ///validates the HTTP is a 101 as per the RFC spec private func validateResponse(buffer: UnsafePointer, bufferLen: Int) -> Bool { - let response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, 0).takeRetainedValue() + let response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, false).takeRetainedValue() CFHTTPMessageAppendBytes(response, buffer, bufferLen) if CFHTTPMessageGetResponseStatusCode(response) != 101 { return false } let cfHeaders = CFHTTPMessageCopyAllHeaderFields(response) - let headers: NSDictionary = cfHeaders.takeRetainedValue() - let acceptKey = headers[headerWSAcceptName] as! NSString + let headers:NSDictionary? = cfHeaders?.takeRetainedValue() + let acceptKey = headers?[headerWSAcceptName] as! NSString if acceptKey.length > 0 { return true } @@ -404,7 +404,7 @@ public class WebSocket : NSObject, NSStreamDelegate { ///process the websocket data private func processRawMessage(buffer: UnsafePointer, bufferLen: Int) { - var response = readStack.last + let response = readStack.last if response != nil && bufferLen < 2 { fragBuffer = NSData(bytes: buffer, length: bufferLen) return @@ -420,7 +420,7 @@ public class WebSocket : NSObject, NSStreamDelegate { resp.bytesLeft -= len resp.buffer?.appendData(NSData(bytes: buffer, length: len)) processResponse(resp) - var offset = bufferLen - extra + let offset = bufferLen - extra if extra > 0 { processExtra((buffer+offset), bufferLen: extra) } @@ -459,7 +459,7 @@ public class WebSocket : NSObject, NSStreamDelegate { if payloadLen == 1 { code = CloseCode.ProtocolError.rawValue } else if payloadLen > 1 { - var codeBuffer = UnsafePointer((buffer+offset)) + let codeBuffer = UnsafePointer((buffer+offset)) code = codeBuffer[0].bigEndian if code < 1000 || (code > 1003 && code < 1007) || (code > 1011 && code < 3000) { code = CloseCode.ProtocolError.rawValue @@ -470,7 +470,7 @@ public class WebSocket : NSObject, NSStreamDelegate { let len = Int(payloadLen-2) if len > 0 { let bytes = UnsafePointer((buffer+offset)) - var str: NSString? = NSString(data: NSData(bytes: bytes, length: len), encoding: NSUTF8StringEncoding) + let str: NSString? = NSString(data: NSData(bytes: bytes, length: len), encoding: NSUTF8StringEncoding) if str == nil { code = CloseCode.ProtocolError.rawValue } @@ -495,7 +495,7 @@ public class WebSocket : NSObject, NSStreamDelegate { dataLength = UInt64(bytes[0].bigEndian) offset += sizeof(UInt16) } - if bufferLen < offset { + if bufferLen < offset || UInt64(bufferLen - offset) < dataLength { fragBuffer = NSData(bytes: buffer, length: bufferLen) return } @@ -599,16 +599,16 @@ public class WebSocket : NSObject, NSStreamDelegate { let data = response.buffer! //local copy so it is perverse for writing dequeueWrite(data, code: OpCode.Pong) } else if response.code == .TextFrame { - var str: NSString? = NSString(data: response.buffer!, encoding: NSUTF8StringEncoding) + let str: NSString? = NSString(data: response.buffer!, encoding: NSUTF8StringEncoding) if str == nil { writeError(CloseCode.Encoding.rawValue) return false } dispatch_async(queue,{ if let textBlock = self.onText { - textBlock(str! as! String) + textBlock(str! as String) } - self.delegate?.websocketDidReceiveMessage(self, text: str! as! String) + self.delegate?.websocketDidReceiveMessage(self, text: str! as String) }) } else if response.code == .BinaryFrame { let data = response.buffer! //local copy so it is perverse for writing @@ -635,7 +635,7 @@ public class WebSocket : NSObject, NSStreamDelegate { ///write a an error to the socket private func writeError(code: UInt16) { let buf = NSMutableData(capacity: sizeof(UInt16)) - var buffer = UnsafeMutablePointer(buf!.bytes) + let buffer = UnsafeMutablePointer(buf!.bytes) buffer[0] = code.bigEndian dequeueWrite(NSData(bytes: buffer, length: sizeof(UInt16)), code: .ConnectionClose) } @@ -670,17 +670,17 @@ public class WebSocket : NSObject, NSStreamDelegate { buffer[1] = CUnsignedChar(dataLength) } else if dataLength <= Int(UInt16.max) { buffer[1] = 126 - var sizeBuffer = UnsafeMutablePointer((buffer+offset)) + let sizeBuffer = UnsafeMutablePointer((buffer+offset)) sizeBuffer[0] = UInt16(dataLength).bigEndian offset += sizeof(UInt16) } else { buffer[1] = 127 - var sizeBuffer = UnsafeMutablePointer((buffer+offset)) + let sizeBuffer = UnsafeMutablePointer((buffer+offset)) sizeBuffer[0] = UInt64(dataLength).bigEndian offset += sizeof(UInt64) } buffer[1] |= self.MaskMask - var maskKey = UnsafeMutablePointer(buffer + offset) + let maskKey = UnsafeMutablePointer(buffer + offset) SecRandomCopyBytes(kSecRandomDefault, Int(sizeof(UInt32)), maskKey) offset += sizeof(UInt32) @@ -694,7 +694,7 @@ public class WebSocket : NSObject, NSStreamDelegate { break } let writeBuffer = UnsafePointer(frame!.bytes+total) - var len = self.outputStream?.write(writeBuffer, maxLength: offset-total) + let len = self.outputStream?.write(writeBuffer, maxLength: offset-total) if len == nil || len! < 0 { var error: NSError? if let streamError = self.outputStream?.streamError { @@ -771,8 +771,8 @@ public class SSLCert { } } -public class Security { - public var validatedDN = true //should the domain name be validated? +private class Security { + private var validatedDN = true //should the domain name be validated? var isReady = false //is the key processing done? var certificates: [NSData]? //the certificates @@ -786,11 +786,11 @@ public class Security { :returns: a representation security object to be used with */ - public convenience init(usePublicKeys: Bool = false) { + private convenience init(usePublicKeys: Bool = false) { let paths = NSBundle.mainBundle().pathsForResourcesOfType("cer", inDirectory: ".") var collect = Array() for path in paths { - if let d = NSData(contentsOfFile: path as! String) { + if let d = NSData(contentsOfFile: path as String) { collect.append(SSLCert(data: d)) } } @@ -805,7 +805,7 @@ public class Security { :returns: a representation security object to be used with */ - public init(certs: [SSLCert], usePublicKeys: Bool) { + private init(certs: [SSLCert], usePublicKeys: Bool) { self.usePublicKeys = usePublicKeys if self.usePublicKeys { @@ -842,7 +842,7 @@ public class Security { :returns: if the key was successfully validated */ - public func isValid(trust: SecTrustRef, domain: String?) -> Bool { + private func isValid(trust: SecTrustRef, domain: String?) -> Bool { var tries = 0 while(!self.isReady) { @@ -854,9 +854,9 @@ public class Security { } var policy: SecPolicyRef if self.validatedDN { - policy = SecPolicyCreateSSL(1, domain).takeRetainedValue() + policy = SecPolicyCreateSSL(true, domain) } else { - policy = SecPolicyCreateBasicX509().takeRetainedValue() + policy = SecPolicyCreateBasicX509() } SecTrustSetPolicies(trust,policy) if self.usePublicKeys { @@ -879,7 +879,7 @@ public class Security { let serverCerts = certificateChainForTrust(trust) var collect = Array() for cert in certs { - collect.append(SecCertificateCreateWithData(nil,cert).takeRetainedValue()) + collect.append(SecCertificateCreateWithData(nil,cert)!) } SecTrustSetAnchorCertificates(trust,collect) var result: SecTrustResultType = 0 @@ -911,10 +911,9 @@ public class Security { :returns: a public key */ func extractPublicKey(data: NSData) -> SecKeyRef? { - var publicKey: NSData? let possibleCert = SecCertificateCreateWithData(nil,data) if let cert = possibleCert { - return extractPublicKeyFromCert(cert.takeRetainedValue(),policy: SecPolicyCreateBasicX509().takeRetainedValue()) + return extractPublicKeyFromCert(cert,policy: SecPolicyCreateBasicX509()) } return nil } @@ -927,13 +926,12 @@ public class Security { :returns: a public key */ func extractPublicKeyFromCert(cert: SecCertificate, policy: SecPolicy) -> SecKeyRef? { - var possibleTrust: Unmanaged? - SecTrustCreateWithCertificates(cert,policy, &possibleTrust) - if let trust = possibleTrust { - let t = trust.takeRetainedValue() + let possibleTrust = UnsafeMutablePointer.alloc(1) + SecTrustCreateWithCertificates( cert, policy, possibleTrust) + if let trust = possibleTrust.memory { var result: SecTrustResultType = 0 - SecTrustEvaluate(t,&result) - return SecTrustCopyPublicKey(t).takeRetainedValue() + SecTrustEvaluate(trust,&result) + return SecTrustCopyPublicKey(trust) } return nil } @@ -949,7 +947,7 @@ public class Security { var collect = Array() for var i = 0; i < SecTrustGetCertificateCount(trust); i++ { let cert = SecTrustGetCertificateAtIndex(trust,i) - collect.append(SecCertificateCopyData(cert.takeRetainedValue()).takeRetainedValue()) + collect.append(SecCertificateCopyData(cert!)) } return collect } @@ -963,10 +961,10 @@ public class Security { */ func publicKeyChainForTrust(trust: SecTrustRef) -> Array { var collect = Array() - let policy = SecPolicyCreateBasicX509().takeRetainedValue() + let policy = SecPolicyCreateBasicX509() for var i = 0; i < SecTrustGetCertificateCount(trust); i++ { let cert = SecTrustGetCertificateAtIndex(trust,i) - if let key = extractPublicKeyFromCert(cert.takeRetainedValue(), policy: policy) { + if let key = extractPublicKeyFromCert(cert!, policy: policy) { collect.append(key) } }