merge master

This commit is contained in:
Erik 2016-01-17 08:48:34 -05:00
commit 03d5f56114
15 changed files with 621 additions and 471 deletions

View File

@ -85,7 +85,7 @@ Carthage
----------------- -----------------
Add this line to your `Cartfile`: Add this line to your `Cartfile`:
``` ```
github "socketio/socket.io-client-swift" ~> 5.0.0 # Or latest version github "socketio/socket.io-client-swift" ~> 5.1.0 # Or latest version
``` ```
Run `carthage update --platform ios,macosx`. Run `carthage update --platform ios,macosx`.
@ -99,7 +99,7 @@ source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0' platform :ios, '8.0'
use_frameworks! use_frameworks!
pod 'Socket.IO-Client-Swift', '~> 5.0.0' # Or latest version pod 'Socket.IO-Client-Swift', '~> 5.1.0' # Or latest version
``` ```
Install pods: Install pods:
@ -127,7 +127,7 @@ CocoaSeeds
Add this line to your `Seedfile`: Add this line to your `Seedfile`:
``` ```
github "socketio/socket.io-client-swift", "v5.0.0", :files => "SocketIOClientSwift/*.swift" # Or latest version github "socketio/socket.io-client-swift", "v5.1.0", :files => "SocketIOClientSwift/*.swift" # Or latest version
``` ```
Run `seed install`. Run `seed install`.
@ -176,7 +176,7 @@ Methods
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. 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. 8. `connect()` - Establishes a connection to the server. A "connect" event is fired upon successful connection.
9. `connect(timeoutAfter timeoutAfter: Int, withTimeoutHandler handler: (() -> Void)?)` - Connect to the server. If it isn't connected after timeoutAfter seconds, the handler is called. 9. `connect(timeoutAfter timeoutAfter: Int, withTimeoutHandler handler: (() -> Void)?)` - Connect to the server. If it isn't connected after timeoutAfter seconds, the handler is called.
10. `close()` - Closes the socket. Once a socket is closed it should not be reopened. 10. `disconnect()` - Closes the socket. Reopening a disconnected socket is not fully tested.
11. `reconnect()` - Causes the client to reconnect to the server. 11. `reconnect()` - Causes the client to reconnect to the server.
12. `joinNamespace(namespace: String)` - Causes the client to join namespace. Shouldn't need to be called unless you change namespaces manually. 12. `joinNamespace(namespace: String)` - Causes the client to join namespace. Shouldn't need to be called unless you change namespaces manually.
13. `leaveNamespace()` - Causes the client to leave the nsp and go back to / 13. `leaveNamespace()` - Causes the client to leave the nsp and go back to /

View File

@ -1,7 +1,7 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = "Socket.IO-Client-Swift" s.name = "Socket.IO-Client-Swift"
s.module_name = "SocketIOClientSwift" s.module_name = "SocketIOClientSwift"
s.version = "5.0.0" s.version = "5.1.0"
s.summary = "Socket.IO-client for iOS and OS X" s.summary = "Socket.IO-client for iOS and OS X"
s.description = <<-DESC s.description = <<-DESC
Socket.IO-client for iOS and OS X. Socket.IO-client for iOS and OS X.
@ -14,7 +14,7 @@ Pod::Spec.new do |s|
s.ios.deployment_target = '8.0' s.ios.deployment_target = '8.0'
s.osx.deployment_target = '10.10' s.osx.deployment_target = '10.10'
s.tvos.deployment_target = '9.0' s.tvos.deployment_target = '9.0'
s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v5.0.0' } s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v5.1.0' }
s.source_files = "Source/**/*.swift" s.source_files = "Source/**/*.swift"
s.requires_arc = true s.requires_arc = true
# s.dependency 'Starscream', '~> 0.9' # currently this repo includes Starscream swift files # s.dependency 'Starscream', '~> 0.9' # currently this repo includes Starscream swift files

View File

@ -17,6 +17,9 @@
57634A2F1BD9B46D00E19CD7 /* SocketBasicPacketTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F124EF1BC574CF002966F4 /* SocketBasicPacketTest.swift */; }; 57634A2F1BD9B46D00E19CD7 /* SocketBasicPacketTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F124EF1BC574CF002966F4 /* SocketBasicPacketTest.swift */; };
57634A321BD9B46D00E19CD7 /* SocketNamespacePacketTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7472C65B1BCAB53E003CA70D /* SocketNamespacePacketTest.swift */; }; 57634A321BD9B46D00E19CD7 /* SocketNamespacePacketTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7472C65B1BCAB53E003CA70D /* SocketNamespacePacketTest.swift */; };
57634A3F1BD9B4BF00E19CD7 /* SocketIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57634A161BD9B46A00E19CD7 /* SocketIO.framework */; }; 57634A3F1BD9B4BF00E19CD7 /* SocketIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57634A161BD9B46A00E19CD7 /* SocketIO.framework */; };
740CA1201C496EEB00CB98F4 /* SocketEngineWebsocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 740CA11F1C496EEB00CB98F4 /* SocketEngineWebsocket.swift */; };
740CA1211C496EF200CB98F4 /* SocketEngineWebsocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 740CA11F1C496EEB00CB98F4 /* SocketEngineWebsocket.swift */; };
740CA1221C496EF700CB98F4 /* SocketEngineWebsocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 740CA11F1C496EEB00CB98F4 /* SocketEngineWebsocket.swift */; };
74171E631C10CD240062D398 /* SocketAckEmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E501C10CD240062D398 /* SocketAckEmitter.swift */; }; 74171E631C10CD240062D398 /* SocketAckEmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E501C10CD240062D398 /* SocketAckEmitter.swift */; };
74171E641C10CD240062D398 /* SocketAckEmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E501C10CD240062D398 /* SocketAckEmitter.swift */; }; 74171E641C10CD240062D398 /* SocketAckEmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E501C10CD240062D398 /* SocketAckEmitter.swift */; };
74171E651C10CD240062D398 /* SocketAckEmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E501C10CD240062D398 /* SocketAckEmitter.swift */; }; 74171E651C10CD240062D398 /* SocketAckEmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E501C10CD240062D398 /* SocketAckEmitter.swift */; };
@ -115,6 +118,9 @@
74171ED41C10CD240062D398 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E621C10CD240062D398 /* WebSocket.swift */; }; 74171ED41C10CD240062D398 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E621C10CD240062D398 /* WebSocket.swift */; };
741F39EE1BD025D80026C9CC /* SocketEngineTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 741F39ED1BD025D80026C9CC /* SocketEngineTest.swift */; }; 741F39EE1BD025D80026C9CC /* SocketEngineTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 741F39ED1BD025D80026C9CC /* SocketEngineTest.swift */; };
741F39EF1BD025D80026C9CC /* SocketEngineTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 741F39ED1BD025D80026C9CC /* SocketEngineTest.swift */; }; 741F39EF1BD025D80026C9CC /* SocketEngineTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 741F39ED1BD025D80026C9CC /* SocketEngineTest.swift */; };
7420CB791C49629E00956AA4 /* SocketEnginePollable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7420CB781C49629E00956AA4 /* SocketEnginePollable.swift */; };
7420CB7A1C49629E00956AA4 /* SocketEnginePollable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7420CB781C49629E00956AA4 /* SocketEnginePollable.swift */; };
7420CB7B1C49629E00956AA4 /* SocketEnginePollable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7420CB781C49629E00956AA4 /* SocketEnginePollable.swift */; };
74321DCB1C2D939A00CF6F43 /* SocketAckManagerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74321DC91C2D939A00CF6F43 /* SocketAckManagerTest.swift */; }; 74321DCB1C2D939A00CF6F43 /* SocketAckManagerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74321DC91C2D939A00CF6F43 /* SocketAckManagerTest.swift */; };
74321DCC1C2D939A00CF6F43 /* SocketParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74321DCA1C2D939A00CF6F43 /* SocketParserTest.swift */; }; 74321DCC1C2D939A00CF6F43 /* SocketParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74321DCA1C2D939A00CF6F43 /* SocketParserTest.swift */; };
7471CCEA1C39926300364B59 /* SocketClientSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74ABF7761C3991C10078C657 /* SocketClientSpec.swift */; }; 7471CCEA1C39926300364B59 /* SocketClientSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74ABF7761C3991C10078C657 /* SocketClientSpec.swift */; };
@ -165,6 +171,7 @@
572EF2481B51F18A00EEBB58 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 572EF2481B51F18A00EEBB58 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
57634A161BD9B46A00E19CD7 /* SocketIO.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SocketIO.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 57634A161BD9B46A00E19CD7 /* SocketIO.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SocketIO.framework; sourceTree = BUILT_PRODUCTS_DIR; };
57634A3B1BD9B46D00E19CD7 /* SocketIO-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SocketIO-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 57634A3B1BD9B46D00E19CD7 /* SocketIO-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SocketIO-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
740CA11F1C496EEB00CB98F4 /* SocketEngineWebsocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SocketEngineWebsocket.swift; path = Source/SocketEngineWebsocket.swift; sourceTree = "<group>"; };
74171E501C10CD240062D398 /* SocketAckEmitter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketAckEmitter.swift; path = Source/SocketAckEmitter.swift; sourceTree = "<group>"; }; 74171E501C10CD240062D398 /* SocketAckEmitter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketAckEmitter.swift; path = Source/SocketAckEmitter.swift; sourceTree = "<group>"; };
74171E511C10CD240062D398 /* SocketAckManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketAckManager.swift; path = Source/SocketAckManager.swift; sourceTree = "<group>"; }; 74171E511C10CD240062D398 /* SocketAckManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketAckManager.swift; path = Source/SocketAckManager.swift; sourceTree = "<group>"; };
74171E521C10CD240062D398 /* SocketAnyEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketAnyEvent.swift; path = Source/SocketAnyEvent.swift; sourceTree = "<group>"; }; 74171E521C10CD240062D398 /* SocketAnyEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketAnyEvent.swift; path = Source/SocketAnyEvent.swift; sourceTree = "<group>"; };
@ -185,6 +192,7 @@
74171E611C10CD240062D398 /* SwiftRegex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftRegex.swift; path = Source/SwiftRegex.swift; sourceTree = "<group>"; }; 74171E611C10CD240062D398 /* SwiftRegex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftRegex.swift; path = Source/SwiftRegex.swift; sourceTree = "<group>"; };
74171E621C10CD240062D398 /* WebSocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WebSocket.swift; path = Source/WebSocket.swift; sourceTree = "<group>"; }; 74171E621C10CD240062D398 /* WebSocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WebSocket.swift; path = Source/WebSocket.swift; sourceTree = "<group>"; };
741F39ED1BD025D80026C9CC /* SocketEngineTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEngineTest.swift; sourceTree = "<group>"; }; 741F39ED1BD025D80026C9CC /* SocketEngineTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEngineTest.swift; sourceTree = "<group>"; };
7420CB781C49629E00956AA4 /* SocketEnginePollable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketEnginePollable.swift; path = Source/SocketEnginePollable.swift; sourceTree = "<group>"; };
74321DC91C2D939A00CF6F43 /* SocketAckManagerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketAckManagerTest.swift; sourceTree = "<group>"; }; 74321DC91C2D939A00CF6F43 /* SocketAckManagerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketAckManagerTest.swift; sourceTree = "<group>"; };
74321DCA1C2D939A00CF6F43 /* SocketParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketParserTest.swift; sourceTree = "<group>"; }; 74321DCA1C2D939A00CF6F43 /* SocketParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketParserTest.swift; sourceTree = "<group>"; };
7472C65B1BCAB53E003CA70D /* SocketNamespacePacketTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketNamespacePacketTest.swift; sourceTree = "<group>"; }; 7472C65B1BCAB53E003CA70D /* SocketNamespacePacketTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketNamespacePacketTest.swift; sourceTree = "<group>"; };
@ -349,7 +357,9 @@
74171E531C10CD240062D398 /* SocketEngine.swift */, 74171E531C10CD240062D398 /* SocketEngine.swift */,
74171E541C10CD240062D398 /* SocketEngineClient.swift */, 74171E541C10CD240062D398 /* SocketEngineClient.swift */,
74171E551C10CD240062D398 /* SocketEnginePacketType.swift */, 74171E551C10CD240062D398 /* SocketEnginePacketType.swift */,
7420CB781C49629E00956AA4 /* SocketEnginePollable.swift */,
74171E561C10CD240062D398 /* SocketEngineSpec.swift */, 74171E561C10CD240062D398 /* SocketEngineSpec.swift */,
740CA11F1C496EEB00CB98F4 /* SocketEngineWebsocket.swift */,
74171E571C10CD240062D398 /* SocketEventHandler.swift */, 74171E571C10CD240062D398 /* SocketEventHandler.swift */,
74171E581C10CD240062D398 /* SocketFixUTF8.swift */, 74171E581C10CD240062D398 /* SocketFixUTF8.swift */,
74171E591C10CD240062D398 /* SocketIOClient.swift */, 74171E591C10CD240062D398 /* SocketIOClient.swift */,
@ -599,10 +609,12 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
740CA1221C496EF700CB98F4 /* SocketEngineWebsocket.swift in Sources */,
74171E931C10CD240062D398 /* SocketFixUTF8.swift in Sources */, 74171E931C10CD240062D398 /* SocketFixUTF8.swift in Sources */,
74171EA51C10CD240062D398 /* SocketIOClientStatus.swift in Sources */, 74171EA51C10CD240062D398 /* SocketIOClientStatus.swift in Sources */,
74171E751C10CD240062D398 /* SocketEngine.swift in Sources */, 74171E751C10CD240062D398 /* SocketEngine.swift in Sources */,
74171E691C10CD240062D398 /* SocketAckManager.swift in Sources */, 74171E691C10CD240062D398 /* SocketAckManager.swift in Sources */,
7420CB791C49629E00956AA4 /* SocketEnginePollable.swift in Sources */,
74ABF7771C3991C10078C657 /* SocketClientSpec.swift in Sources */, 74ABF7771C3991C10078C657 /* SocketClientSpec.swift in Sources */,
74171E871C10CD240062D398 /* SocketEngineSpec.swift in Sources */, 74171E871C10CD240062D398 /* SocketEngineSpec.swift in Sources */,
74171E631C10CD240062D398 /* SocketAckEmitter.swift in Sources */, 74171E631C10CD240062D398 /* SocketAckEmitter.swift in Sources */,
@ -656,10 +668,12 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
740CA1211C496EF200CB98F4 /* SocketEngineWebsocket.swift in Sources */,
7471CCEA1C39926300364B59 /* SocketClientSpec.swift in Sources */, 7471CCEA1C39926300364B59 /* SocketClientSpec.swift in Sources */,
74171E951C10CD240062D398 /* SocketFixUTF8.swift in Sources */, 74171E951C10CD240062D398 /* SocketFixUTF8.swift in Sources */,
74171EA71C10CD240062D398 /* SocketIOClientStatus.swift in Sources */, 74171EA71C10CD240062D398 /* SocketIOClientStatus.swift in Sources */,
74171E771C10CD240062D398 /* SocketEngine.swift in Sources */, 74171E771C10CD240062D398 /* SocketEngine.swift in Sources */,
7420CB7A1C49629E00956AA4 /* SocketEnginePollable.swift in Sources */,
74171E6B1C10CD240062D398 /* SocketAckManager.swift in Sources */, 74171E6B1C10CD240062D398 /* SocketAckManager.swift in Sources */,
74171E891C10CD240062D398 /* SocketEngineSpec.swift in Sources */, 74171E891C10CD240062D398 /* SocketEngineSpec.swift in Sources */,
74171E651C10CD240062D398 /* SocketAckEmitter.swift in Sources */, 74171E651C10CD240062D398 /* SocketAckEmitter.swift in Sources */,
@ -697,10 +711,12 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
740CA1201C496EEB00CB98F4 /* SocketEngineWebsocket.swift in Sources */,
7471CCEB1C39926C00364B59 /* SocketClientSpec.swift in Sources */, 7471CCEB1C39926C00364B59 /* SocketClientSpec.swift in Sources */,
74171E971C10CD240062D398 /* SocketFixUTF8.swift in Sources */, 74171E971C10CD240062D398 /* SocketFixUTF8.swift in Sources */,
74171EA91C10CD240062D398 /* SocketIOClientStatus.swift in Sources */, 74171EA91C10CD240062D398 /* SocketIOClientStatus.swift in Sources */,
74171E791C10CD240062D398 /* SocketEngine.swift in Sources */, 74171E791C10CD240062D398 /* SocketEngine.swift in Sources */,
7420CB7B1C49629E00956AA4 /* SocketEnginePollable.swift in Sources */,
74171E6D1C10CD240062D398 /* SocketAckManager.swift in Sources */, 74171E6D1C10CD240062D398 /* SocketAckManager.swift in Sources */,
74171E8B1C10CD240062D398 /* SocketEngineSpec.swift in Sources */, 74171E8B1C10CD240062D398 /* SocketEngineSpec.swift in Sources */,
74171E671C10CD240062D398 /* SocketAckEmitter.swift in Sources */, 74171E671C10CD240062D398 /* SocketAckEmitter.swift in Sources */,

View File

@ -15,7 +15,7 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "572EF2371B51F18A00EEBB58" BlueprintIdentifier = "572EF2371B51F18A00EEBB58"
BuildableName = "SocketIO.framework" BuildableName = "SocketIOClientSwift.framework"
BlueprintName = "SocketIO-Mac" BlueprintName = "SocketIO-Mac"
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj"> ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
</BuildableReference> </BuildableReference>
@ -57,7 +57,7 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "572EF2371B51F18A00EEBB58" BlueprintIdentifier = "572EF2371B51F18A00EEBB58"
BuildableName = "SocketIO.framework" BuildableName = "SocketIOClientSwift.framework"
BlueprintName = "SocketIO-Mac" BlueprintName = "SocketIO-Mac"
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj"> ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
</BuildableReference> </BuildableReference>
@ -79,7 +79,7 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "572EF2371B51F18A00EEBB58" BlueprintIdentifier = "572EF2371B51F18A00EEBB58"
BuildableName = "SocketIO.framework" BuildableName = "SocketIOClientSwift.framework"
BlueprintName = "SocketIO-Mac" BlueprintName = "SocketIO-Mac"
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj"> ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
</BuildableReference> </BuildableReference>
@ -97,7 +97,7 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "572EF2371B51F18A00EEBB58" BlueprintIdentifier = "572EF2371B51F18A00EEBB58"
BuildableName = "SocketIO.framework" BuildableName = "SocketIOClientSwift.framework"
BlueprintName = "SocketIO-Mac" BlueprintName = "SocketIO-Mac"
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj"> ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
</BuildableReference> </BuildableReference>

View File

@ -15,7 +15,7 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "572EF2181B51F16C00EEBB58" BlueprintIdentifier = "572EF2181B51F16C00EEBB58"
BuildableName = "SocketIO.framework" BuildableName = "SocketIOClientSwift.framework"
BlueprintName = "SocketIO-iOS" BlueprintName = "SocketIO-iOS"
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj"> ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
</BuildableReference> </BuildableReference>
@ -58,7 +58,7 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "572EF2181B51F16C00EEBB58" BlueprintIdentifier = "572EF2181B51F16C00EEBB58"
BuildableName = "SocketIO.framework" BuildableName = "SocketIOClientSwift.framework"
BlueprintName = "SocketIO-iOS" BlueprintName = "SocketIO-iOS"
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj"> ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
</BuildableReference> </BuildableReference>
@ -80,7 +80,7 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "572EF2181B51F16C00EEBB58" BlueprintIdentifier = "572EF2181B51F16C00EEBB58"
BuildableName = "SocketIO.framework" BuildableName = "SocketIOClientSwift.framework"
BlueprintName = "SocketIO-iOS" BlueprintName = "SocketIO-iOS"
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj"> ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
</BuildableReference> </BuildableReference>
@ -98,7 +98,7 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "572EF2181B51F16C00EEBB58" BlueprintIdentifier = "572EF2181B51F16C00EEBB58"
BuildableName = "SocketIO.framework" BuildableName = "SocketIOClientSwift.framework"
BlueprintName = "SocketIO-iOS" BlueprintName = "SocketIO-iOS"
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj"> ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
</BuildableReference> </BuildableReference>

View File

@ -50,4 +50,28 @@ class SocketEngineTest: XCTestCase {
engine.parsePollingMessage("15:42[\"blankTest\"]24:42[\"stringTest\",\"hello\"]") engine.parsePollingMessage("15:42[\"blankTest\"]24:42[\"stringTest\",\"hello\"]")
waitForExpectationsWithTimeout(3, handler: nil) waitForExpectationsWithTimeout(3, handler: nil)
} }
func testEngineDoesErrorOnUnknownTransport() {
let finalExpectation = expectationWithDescription("Unknown Transport")
client.on("error") {data, ack in
if let error = data[0] as? String where error == "Unknown transport" {
finalExpectation.fulfill()
}
}
engine.parseEngineMessage("{\"code\": 0, \"message\": \"Unknown transport\"}", fromPolling: false)
waitForExpectationsWithTimeout(3, handler: nil)
}
func testEngineDoesErrorOnUnknownMessage() {
let finalExpectation = expectationWithDescription("Engine Errors")
client.on("error") {data, ack in
finalExpectation.fulfill()
}
engine.parseEngineMessage("afafafda", fromPolling: false)
waitForExpectationsWithTimeout(3, handler: nil)
}
} }

View File

@ -100,6 +100,18 @@ class SocketSideEffectTest: XCTestCase {
XCTAssertEqual(socket.testHandlers.count, 1) XCTAssertEqual(socket.testHandlers.count, 1)
} }
func testHandlesErrorPacket() {
let expectation = expectationWithDescription("Handled error")
socket.on("error") {data, ack in
if let error = data[0] as? String where error == "test error" {
expectation.fulfill()
}
}
socket.parseSocketMessage("4\"test error\"")
waitForExpectationsWithTimeout(3, handler: nil)
}
func testHandleBinaryEvent() { func testHandleBinaryEvent() {
let expectation = expectationWithDescription("handled binary event") let expectation = expectationWithDescription("handled binary event")
socket.on("test") {data, ack in socket.on("test") {data, ack in

View File

@ -28,8 +28,16 @@ protocol SocketClientSpec: class {
func didConnect() func didConnect()
func didDisconnect(reason: String) func didDisconnect(reason: String)
func didError(reason: AnyObject) func didError(reason: String)
func handleAck(ack: Int, data: [AnyObject]) func handleAck(ack: Int, data: [AnyObject])
func handleEvent(event: String, data: [AnyObject], isInternalMessage: Bool, withAck ack: Int) func handleEvent(event: String, data: [AnyObject], isInternalMessage: Bool, withAck ack: Int)
func joinNamespace(namespace: String) func joinNamespace(namespace: String)
} }
extension SocketClientSpec {
func didError(reason: String) {
DefaultSocketLogger.Logger.error("%@", type: "SocketIOClient", args: reason)
handleEvent("error", data: [reason], isInternalMessage: true, withAck: -1)
}
}

View File

@ -24,12 +24,32 @@
import Foundation import Foundation
public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate { public final class SocketEngine: NSObject, SocketEnginePollable, SocketEngineWebsocket {
public private(set) var sid = "" public let emitQueue = dispatch_queue_create("com.socketio.engineEmitQueue", DISPATCH_QUEUE_SERIAL)
public let handleQueue = dispatch_queue_create("com.socketio.engineHandleQueue", DISPATCH_QUEUE_SERIAL)
public let parseQueue = dispatch_queue_create("com.socketio.engineParseQueue", DISPATCH_QUEUE_SERIAL)
public var postWait = [String]()
public var waitingForPoll = false
public var waitingForPost = false
public private(set) var closed = false
public private(set) var connected = false
public private(set) var cookies: [NSHTTPCookie]? public private(set) var cookies: [NSHTTPCookie]?
public private(set) var extraHeaders: [String: String]?
public private(set) var fastUpgrade = false
public private(set) var forcePolling = false
public private(set) var forceWebsockets = false
public private(set) var invalidated = false
public private(set) var pingTimer: NSTimer?
public private(set) var polling = true
public private(set) var probing = false
public private(set) var session: NSURLSession?
public private(set) var sid = ""
public private(set) var socketPath = "/engine.io" public private(set) var socketPath = "/engine.io"
public private(set) var urlPolling = "" public private(set) var urlPolling = ""
public private(set) var urlWebSocket = "" public private(set) var urlWebSocket = ""
public private(set) var websocket = false
public private(set) var ws: WebSocket? public private(set) var ws: WebSocket?
public weak var client: SocketEngineClient? public weak var client: SocketEngineClient?
@ -40,22 +60,11 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate {
private typealias ProbeWaitQueue = [Probe] private typealias ProbeWaitQueue = [Probe]
private let allowedCharacterSet = NSCharacterSet(charactersInString: "!*'();:@&=+$,/?%#[]\" {}").invertedSet private let allowedCharacterSet = NSCharacterSet(charactersInString: "!*'();:@&=+$,/?%#[]\" {}").invertedSet
private let emitQueue = dispatch_queue_create("com.socketio.engineEmitQueue", DISPATCH_QUEUE_SERIAL)
private let handleQueue = dispatch_queue_create("com.socketio.engineHandleQueue", DISPATCH_QUEUE_SERIAL)
private let logType = "SocketEngine" private let logType = "SocketEngine"
private let parseQueue = dispatch_queue_create("com.socketio.engineParseQueue", DISPATCH_QUEUE_SERIAL)
private let url: String private let url: String
private let workQueue = NSOperationQueue()
private var connectParams: [String: AnyObject]? private var connectParams: [String: AnyObject]?
private var closed = false
private var extraHeaders: [String: String]?
private var fastUpgrade = false
private var forcePolling = false
private var forceWebsockets = false
private var invalidated = false
private var pingInterval: Double? private var pingInterval: Double?
private var pingTimer: NSTimer?
private var pingTimeout = 0.0 { private var pingTimeout = 0.0 {
didSet { didSet {
pongsMissedMax = Int(pingTimeout / (pingInterval ?? 25)) pongsMissedMax = Int(pingTimeout / (pingInterval ?? 25))
@ -63,19 +72,10 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate {
} }
private var pongsMissed = 0 private var pongsMissed = 0
private var pongsMissedMax = 0 private var pongsMissedMax = 0
private var postWait = [String]()
private var probing = false
private var probeWait = ProbeWaitQueue() private var probeWait = ProbeWaitQueue()
private var secure = false private var secure = false
private var selfSigned = false private var selfSigned = false
private var session: NSURLSession?
private var voipEnabled = false private var voipEnabled = false
private var waitingForPoll = false
private var waitingForPost = false
private var websocketConnected = false
private(set) var connected = false
private(set) var polling = true
private(set) var websocket = false
public init(client: SocketEngineClient, url: String, options: Set<SocketIOClientOption>) { public init(client: SocketEngineClient, url: String, options: Set<SocketIOClientOption>) {
self.client = client self.client = client
@ -130,20 +130,19 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate {
switch code { switch code {
case 0: // Unknown transport case 0: // Unknown transport
logAndError(error) didError(error)
case 1: // Unknown sid. clear and retry connect case 1: // Unknown sid. clear and retry connect
sid = "" didError(error)
open(connectParams)
case 2: // Bad handshake request case 2: // Bad handshake request
logAndError(error) didError(error)
case 3: // Bad request case 3: // Bad request
logAndError(error) didError(error)
default: default:
logAndError(error) didError(error)
} }
} }
} catch { } catch {
logAndError("Got unknown error from server") didError("Got unknown error from server \(msg)")
} }
} }
@ -163,37 +162,29 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate {
} }
} }
public func close() { public func close(reason: String) {
DefaultSocketLogger.Logger.log("Engine is being closed.", type: logType) func postSendClose(data: NSData?, _ res: NSURLResponse?, _ err: NSError?) {
sid = ""
closed = true
invalidated = true
connected = false
pingTimer?.invalidate() pingTimer?.invalidate()
closed = true ws?.disconnect()
connected = false stopPolling()
client?.engineDidClose(reason)
}
DefaultSocketLogger.Logger.log("Engine is being closed.", type: logType)
if websocket { if websocket {
sendWebSocketMessage("", withType: .Close, withData: []) sendWebSocketMessage("", withType: .Close, withData: [])
postSendClose(nil, nil, nil)
} else { } else {
sendPollMessage("", withType: .Close, withData: []) // We need to take special care when we're polling that we send it ASAP
} postWait.append(String(SocketEnginePacketType.Close.rawValue))
let req = createRequestForPostWithPostWait()
ws?.disconnect() doRequest(req, withCallback: postSendClose)
stopPolling()
client?.engineDidClose("Disconnect")
}
private func createBinaryDataForSend(data: NSData) -> Either<NSData, String> {
if websocket {
var byteArray = [UInt8](count: 1, repeatedValue: 0x0)
byteArray[0] = 4
let mutData = NSMutableData(bytes: &byteArray, length: 1)
mutData.appendData(data)
return .Left(mutData)
} else {
let str = "b4" + data.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
return .Right(str)
} }
} }
@ -264,7 +255,13 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate {
} }
} }
private func doFastUpgrade() { public func didError(error: String) {
DefaultSocketLogger.Logger.error(error, type: logType)
client?.engineDidError(error)
close(error)
}
public func doFastUpgrade() {
if waitingForPoll { if waitingForPoll {
DefaultSocketLogger.Logger.error("Outstanding poll when switched to WebSockets," + DefaultSocketLogger.Logger.error("Outstanding poll when switched to WebSockets," +
"we'll probably disconnect soon. You should report this.", type: logType) "we'll probably disconnect soon. You should report this.", type: logType)
@ -294,6 +291,18 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate {
} }
} }
// We had packets waiting for send when we upgraded
// Send them raw
public func flushWaitingForPostToWebSocket() {
guard let ws = self.ws else { return }
for msg in postWait {
ws.writeString(fixDoubleUTF8(msg))
}
postWait.removeAll(keepCapacity: true)
}
private func handleClose(reason: String) { private func handleClose(reason: String) {
client?.engineDidClose(reason) client?.engineDidClose(reason)
} }
@ -318,7 +327,7 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate {
connected = true connected = true
if let upgrades = json?["upgrades"] as? [String] { if let upgrades = json?["upgrades"] as? [String] {
upgradeWs = upgrades.filter {$0 == "websocket"}.count != 0 upgradeWs = upgrades.contains("websocket")
} else { } else {
upgradeWs = false upgradeWs = false
} }
@ -331,17 +340,20 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate {
if !forcePolling && !forceWebsockets && upgradeWs { if !forcePolling && !forceWebsockets && upgradeWs {
createWebsocketAndConnect(true) createWebsocketAndConnect(true)
} }
startPingTimer()
if !forceWebsockets {
doPoll()
}
client?.engineDidOpen?("Connect")
} }
} catch { } catch {
DefaultSocketLogger.Logger.error("Error parsing open packet", type: logType) didError("Error parsing open packet")
return return
} }
startPingTimer()
if !forceWebsockets {
doPoll()
}
} }
private func handlePong(pongMessage: String) { private func handlePong(pongMessage: String) {
@ -353,35 +365,15 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate {
} }
} }
// A poll failed, tell the client about it public func open(opts: [String: AnyObject]?) {
private func handlePollingFailed(reason: String) {
connected = false
ws?.disconnect()
pingTimer?.invalidate()
waitingForPoll = false
waitingForPost = false
if !closed {
client?.didError(reason)
client?.engineDidClose(reason)
}
}
private func logAndError(error: String) {
DefaultSocketLogger.Logger.error(error, type: logType)
client?.didError(error)
}
public func open(opts: [String: AnyObject]? = nil) {
connectParams = opts
if connected { if connected {
DefaultSocketLogger.Logger.error("Tried to open while connected", type: logType) DefaultSocketLogger.Logger.error("Engine tried opening while connected. This is probably a programming error. "
client?.didError("Tried to open engine while connected") + "Abandoning open attempt", type: logType)
return return
} }
connectParams = opts
DefaultSocketLogger.Logger.log("Starting engine", type: logType) DefaultSocketLogger.Logger.log("Starting engine", type: logType)
DefaultSocketLogger.Logger.log("Handshaking", type: logType) DefaultSocketLogger.Logger.log("Handshaking", type: logType)
@ -412,12 +404,12 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate {
doLongPoll(reqPolling) doLongPoll(reqPolling)
} }
private func parseEngineData(data: NSData) { public func parseEngineData(data: NSData) {
DefaultSocketLogger.Logger.log("Got binary data: %@", type: "SocketEngine", args: data) DefaultSocketLogger.Logger.log("Got binary data: %@", type: "SocketEngine", args: data)
client?.parseEngineBinaryData(data.subdataWithRange(NSMakeRange(1, data.length - 1))) client?.parseEngineBinaryData(data.subdataWithRange(NSMakeRange(1, data.length - 1)))
} }
private func parseEngineMessage(message: String, fromPolling: Bool) { public func parseEngineMessage(message: String, fromPolling: Bool) {
DefaultSocketLogger.Logger.log("Got message: %@", type: logType, args: message) DefaultSocketLogger.Logger.log("Got message: %@", type: logType, args: message)
let reader = SocketStringReader(message: message) let reader = SocketStringReader(message: message)
@ -453,13 +445,6 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate {
} }
} }
private func probeWebSocket() {
if websocketConnected {
sendWebSocketMessage("probe", withType: .Ping, withData: [])
}
}
private func resetEngine() { private func resetEngine() {
closed = false closed = false
connected = false connected = false
@ -469,12 +454,11 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate {
invalidated = false invalidated = false
session = NSURLSession(configuration: .defaultSessionConfiguration(), session = NSURLSession(configuration: .defaultSessionConfiguration(),
delegate: sessionDelegate, delegate: sessionDelegate,
delegateQueue: workQueue) delegateQueue: NSOperationQueue())
sid = "" sid = ""
waitingForPoll = false waitingForPoll = false
waitingForPost = false waitingForPost = false
websocket = false websocket = false
websocketConnected = false
} }
/// Send an engine message (4) /// Send an engine message (4)
@ -512,7 +496,7 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate {
} }
private func upgradeTransport() { private func upgradeTransport() {
if websocketConnected { if ws?.isConnected ?? false {
DefaultSocketLogger.Logger.log("Upgrading transport to WebSockets", type: logType) DefaultSocketLogger.Logger.log("Upgrading transport to WebSockets", type: logType)
fastUpgrade = true fastUpgrade = true
@ -539,222 +523,9 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate {
} }
} }
} }
}
// Polling methods
extension SocketEngine {
private func addHeaders(req: NSMutableURLRequest) {
if cookies != nil {
let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies!)
req.allHTTPHeaderFields = headers
}
if extraHeaders != nil {
for (headerName, value) in extraHeaders! {
req.setValue(value, forHTTPHeaderField: headerName)
}
}
}
private func doPoll() {
if websocket || waitingForPoll || !connected || closed {
return
}
waitingForPoll = true
let req = NSMutableURLRequest(URL: NSURL(string: urlPolling + "&sid=\(sid)&b64=1")!)
addHeaders(req)
doLongPoll(req)
}
private func doRequest(req: NSURLRequest,
withCallback callback: (NSData?, NSURLResponse?, NSError?) -> Void) {
if !polling || closed || invalidated {
DefaultSocketLogger.Logger.error("Tried to do polling request when not supposed to", type: logType)
return
}
DefaultSocketLogger.Logger.log("Doing polling request", type: logType)
session?.dataTaskWithRequest(req, completionHandler: callback).resume()
}
private func doLongPoll(req: NSURLRequest) {
doRequest(req) {[weak self] data, res, err in
guard let this = self else {return}
if err != nil || data == nil {
DefaultSocketLogger.Logger.error(err?.localizedDescription ?? "Error", type: this.logType)
if this.polling {
this.handlePollingFailed(err?.localizedDescription ?? "Error")
}
return
}
DefaultSocketLogger.Logger.log("Got polling response", type: this.logType)
if let str = String(data: data!, encoding: NSUTF8StringEncoding) {
dispatch_async(this.parseQueue) {
this.parsePollingMessage(str)
}
}
this.waitingForPoll = false
if this.fastUpgrade {
this.doFastUpgrade()
} else if !this.closed && this.polling {
this.doPoll()
}
}
}
private func flushWaitingForPost() {
if postWait.count == 0 || !connected {
return
} else if websocket {
flushWaitingForPostToWebSocket()
return
}
var postStr = ""
for packet in postWait {
let len = packet.characters.count
postStr += "\(len):\(packet)"
}
postWait.removeAll(keepCapacity: false)
let req = NSMutableURLRequest(URL: NSURL(string: urlPolling + "&sid=\(sid)")!)
addHeaders(req)
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
DefaultSocketLogger.Logger.log("POSTing: %@", type: logType, args: postStr)
doRequest(req) {[weak self] data, res, err in
guard let this = self else {return}
if err != nil {
DefaultSocketLogger.Logger.error(err?.localizedDescription ?? "Error", type: this.logType)
if this.polling {
this.handlePollingFailed(err?.localizedDescription ?? "Error")
}
return
}
this.waitingForPost = false
dispatch_async(this.emitQueue) {
if !this.fastUpgrade {
this.flushWaitingForPost()
this.doPoll()
}
}
}
}
// We had packets waiting for send when we upgraded
// Send them raw
private func flushWaitingForPostToWebSocket() {
guard let ws = self.ws else { return }
for msg in postWait {
ws.writeString(fixDoubleUTF8(msg))
}
postWait.removeAll(keepCapacity: true)
}
func parsePollingMessage(str: String) {
guard str.characters.count != 1 else {
return
}
var reader = SocketStringReader(message: str)
while reader.hasNext {
if let n = Int(reader.readUntilStringOccurence(":")) {
let str = reader.read(n)
dispatch_async(handleQueue) {
self.parseEngineMessage(str, fromPolling: true)
}
} else {
dispatch_async(handleQueue) {
self.parseEngineMessage(str, fromPolling: true)
}
break
}
}
}
/// Send polling message.
/// Only call on emitQueue
private func sendPollMessage(message: String, withType type: SocketEnginePacketType,
withData datas: [NSData]) {
DefaultSocketLogger.Logger.log("Sending poll: %@ as type: %@", type: logType, args: message, type.rawValue)
let fixedMessage = doubleEncodeUTF8(message)
let strMsg = "\(type.rawValue)\(fixedMessage)"
postWait.append(strMsg)
for data in datas {
if case let .Right(bin) = createBinaryDataForSend(data) {
postWait.append(bin)
}
}
if !waitingForPost {
flushWaitingForPost()
}
}
private func stopPolling() {
invalidated = true
session?.finishTasksAndInvalidate()
}
}
// WebSocket methods
extension SocketEngine {
/// Send message on WebSockets
/// Only call on emitQueue
private func sendWebSocketMessage(str: String, withType type: SocketEnginePacketType,
withData datas: [NSData]) {
DefaultSocketLogger.Logger.log("Sending ws: %@ as type: %@", type: logType, args: str, type.rawValue)
ws?.writeString("\(type.rawValue)\(str)")
for data in datas {
if case let .Left(bin) = createBinaryDataForSend(data) {
ws?.writeData(bin)
}
}
}
// Delagate methods
public func websocketDidConnect(socket:WebSocket) {
websocketConnected = true
// Delegate methods
public func websocketDidConnect(socket: WebSocket) {
if !forceWebsockets { if !forceWebsockets {
probing = true probing = true
probeWebSocket() probeWebSocket()
@ -766,7 +537,6 @@ extension SocketEngine {
} }
public func websocketDidDisconnect(socket: WebSocket, error: NSError?) { public func websocketDidDisconnect(socket: WebSocket, error: NSError?) {
websocketConnected = false
probing = false probing = false
if closed { if closed {
@ -782,7 +552,7 @@ extension SocketEngine {
let reason = error?.localizedDescription ?? "Socket Disconnected" let reason = error?.localizedDescription ?? "Socket Disconnected"
if error != nil { if error != nil {
client?.didError(reason) didError(reason)
} }
client?.engineDidClose(reason) client?.engineDidClose(reason)
@ -790,12 +560,4 @@ extension SocketEngine {
flushProbeWait() flushProbeWait()
} }
} }
public func websocketDidReceiveMessage(socket: WebSocket, text: String) {
parseEngineMessage(text, fromPolling: false)
}
public func websocketDidReceiveData(socket: WebSocket, data: NSData) {
parseEngineData(data)
}
} }

View File

@ -26,8 +26,9 @@
import Foundation import Foundation
@objc public protocol SocketEngineClient { @objc public protocol SocketEngineClient {
func didError(reason: AnyObject) func engineDidError(reason: String)
func engineDidClose(reason: String) func engineDidClose(reason: String)
optional func engineDidOpen(reason: String)
func parseEngineMessage(msg: String) func parseEngineMessage(msg: String)
func parseEngineBinaryData(data: NSData) func parseEngineBinaryData(data: NSData)
} }

View File

@ -0,0 +1,231 @@
//
// SocketEnginePollable.swift
// Socket.IO-Client-Swift
//
// Created by Erik Little on 1/15/16.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
/// Protocol that is used to implement socket.io polling support
public protocol SocketEnginePollable: SocketEngineSpec {
var invalidated: Bool { get }
/// Holds strings waiting to be sent over polling.
/// You shouldn't need to mess with this.
var postWait: [String] { get set }
var session: NSURLSession? { get }
/// Because socket.io doesn't let you send two polling request at the same time
/// we have to keep track if there's an outstanding poll
var waitingForPoll: Bool { get set }
/// Because socket.io doesn't let you send two post request at the same time
/// we have to keep track if there's an outstanding post
var waitingForPost: Bool { get set }
func doPoll()
func sendPollMessage(message: String, withType type: SocketEnginePacketType, withData datas: [NSData])
func stopPolling()
}
// Default polling methods
extension SocketEnginePollable {
private func addHeaders(req: NSMutableURLRequest) {
if cookies != nil {
let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies!)
req.allHTTPHeaderFields = headers
}
if extraHeaders != nil {
for (headerName, value) in extraHeaders! {
req.setValue(value, forHTTPHeaderField: headerName)
}
}
}
func createRequestForPostWithPostWait() -> NSURLRequest {
var postStr = ""
for packet in postWait {
let len = packet.characters.count
postStr += "\(len):\(packet)"
}
DefaultSocketLogger.Logger.log("Created POST string: %@", type: "SocketEnginePolling", args: postStr)
postWait.removeAll(keepCapacity: false)
let req = NSMutableURLRequest(URL: NSURL(string: urlPolling + "&sid=\(sid)")!)
addHeaders(req)
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")
return req
}
public func doPoll() {
if websocket || waitingForPoll || !connected || closed {
return
}
waitingForPoll = true
let req = NSMutableURLRequest(URL: NSURL(string: urlPolling + "&sid=\(sid)&b64=1")!)
addHeaders(req)
doLongPoll(req)
}
func doRequest(req: NSURLRequest, withCallback callback: (NSData?, NSURLResponse?, NSError?) -> Void) {
if !polling || closed || invalidated {
DefaultSocketLogger.Logger.error("Tried to do polling request when not supposed to", type: "SocketEnginePolling")
return
}
DefaultSocketLogger.Logger.log("Doing polling request", type: "SocketEnginePolling")
session?.dataTaskWithRequest(req, completionHandler: callback).resume()
}
func doLongPoll(req: NSURLRequest) {
doRequest(req) {[weak self] data, res, err in
guard let this = self else {return}
if err != nil || data == nil {
DefaultSocketLogger.Logger.error(err?.localizedDescription ?? "Error", type: "SocketEnginePolling")
if this.polling {
this.didError(err?.localizedDescription ?? "Error")
}
return
}
DefaultSocketLogger.Logger.log("Got polling response", type: "SocketEnginePolling")
if let str = String(data: data!, encoding: NSUTF8StringEncoding) {
dispatch_async(this.parseQueue) {
this.parsePollingMessage(str)
}
}
this.waitingForPoll = false
if this.fastUpgrade {
this.doFastUpgrade()
} else if !this.closed && this.polling {
this.doPoll()
}
}
}
private func flushWaitingForPost() {
if postWait.count == 0 || !connected {
return
} else if websocket {
flushWaitingForPostToWebSocket()
return
}
let req = createRequestForPostWithPostWait()
waitingForPost = true
DefaultSocketLogger.Logger.log("POSTing", type: "SocketEnginePolling")
doRequest(req) {[weak self] data, res, err in
guard let this = self else { return }
if err != nil {
DefaultSocketLogger.Logger.error(err?.localizedDescription ?? "Error", type: "SocketEnginePolling")
if this.polling {
this.didError(err?.localizedDescription ?? "Error")
}
return
}
this.waitingForPost = false
dispatch_async(this.emitQueue) {
if !this.fastUpgrade {
this.flushWaitingForPost()
this.doPoll()
}
}
}
}
func parsePollingMessage(str: String) {
guard str.characters.count != 1 else {
return
}
var reader = SocketStringReader(message: str)
while reader.hasNext {
if let n = Int(reader.readUntilStringOccurence(":")) {
let str = reader.read(n)
dispatch_async(handleQueue) {
self.parseEngineMessage(str, fromPolling: true)
}
} else {
dispatch_async(handleQueue) {
self.parseEngineMessage(str, fromPolling: true)
}
break
}
}
}
/// Send polling message.
/// Only call on emitQueue
public func sendPollMessage(message: String, withType type: SocketEnginePacketType, withData datas: [NSData]) {
DefaultSocketLogger.Logger.log("Sending poll: %@ as type: %@", type: "SocketEnginePolling", args: message, type.rawValue)
let fixedMessage = doubleEncodeUTF8(message)
let strMsg = "\(type.rawValue)\(fixedMessage)"
postWait.append(strMsg)
for data in datas {
if case let .Right(bin) = createBinaryDataForSend(data) {
postWait.append(bin)
}
}
if !waitingForPost {
flushWaitingForPost()
}
}
public func stopPolling() {
waitingForPoll = false
waitingForPost = false
session?.finishTasksAndInvalidate()
}
}

View File

@ -26,17 +26,53 @@
import Foundation import Foundation
@objc public protocol SocketEngineSpec { @objc public protocol SocketEngineSpec {
weak var client: SocketEngineClient? {get set} weak var client: SocketEngineClient? { get set }
var cookies: [NSHTTPCookie]? {get} var closed: Bool { get }
var sid: String {get} var connected: Bool { get }
var socketPath: String {get} var cookies: [NSHTTPCookie]? { get }
var urlPolling: String {get} var extraHeaders: [String: String]? { get }
var urlWebSocket: String {get} var fastUpgrade: Bool { get }
var forcePolling: Bool { get }
var forceWebsockets: Bool { get }
var parseQueue: dispatch_queue_t! { get }
var pingTimer: NSTimer? { get }
var polling: Bool { get }
var probing: Bool { get }
var emitQueue: dispatch_queue_t! { get }
var handleQueue: dispatch_queue_t! { get }
var sid: String { get }
var socketPath: String { get }
var urlPolling: String { get }
var urlWebSocket: String { get }
var websocket: Bool { get }
init(client: SocketEngineClient, url: String, options: NSDictionary?) init(client: SocketEngineClient, url: String, options: NSDictionary?)
func close() func close(reason: String)
func didError(error: String)
func doFastUpgrade()
func flushWaitingForPostToWebSocket()
func open(opts: [String: AnyObject]?) func open(opts: [String: AnyObject]?)
func parseEngineData(data: NSData)
func parseEngineMessage(message: String, fromPolling: Bool)
func send(msg: String, withData datas: [NSData]) func send(msg: String, withData datas: [NSData])
func write(msg: String, withType type: SocketEnginePacketType, withData data: [NSData]) func write(msg: String, withType type: SocketEnginePacketType, withData data: [NSData])
} }
extension SocketEngineSpec {
func createBinaryDataForSend(data: NSData) -> Either<NSData, String> {
if websocket {
var byteArray = [UInt8](count: 1, repeatedValue: 0x0)
byteArray[0] = 4
let mutData = NSMutableData(bytes: &byteArray, length: 1)
mutData.appendData(data)
return .Left(mutData)
} else {
let str = "b4" + data.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
return .Right(str)
}
}
}

View File

@ -0,0 +1,64 @@
//
// SocketEngineWebsocket.swift
// Socket.IO-Client-Swift
//
// Created by Erik Little on 1/15/16.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
/// Protocol that is used to implement socket.io WebSocket support
public protocol SocketEngineWebsocket: SocketEngineSpec, WebSocketDelegate {
var ws: WebSocket? { get }
func sendWebSocketMessage(str: String, withType type: SocketEnginePacketType, withData datas: [NSData])
}
// WebSocket methods
extension SocketEngineWebsocket {
func probeWebSocket() {
if ws?.isConnected ?? false {
sendWebSocketMessage("probe", withType: .Ping, withData: [])
}
}
/// Send message on WebSockets
/// Only call on emitQueue
public func sendWebSocketMessage(str: String, withType type: SocketEnginePacketType, withData datas: [NSData]) {
DefaultSocketLogger.Logger.log("Sending ws: %@ as type: %@", type: "SocketEngine", args: str, type.rawValue)
ws?.writeString("\(type.rawValue)\(str)")
for data in datas {
if case let .Left(bin) = createBinaryDataForSend(data) {
ws?.writeData(bin)
}
}
}
public func websocketDidReceiveMessage(socket: WebSocket, text: String) {
parseEngineMessage(text, fromPolling: false)
}
public func websocketDidReceiveData(socket: WebSocket, data: NSData) {
parseEngineData(data)
}
}

View File

@ -109,7 +109,7 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable
deinit { deinit {
DefaultSocketLogger.Logger.log("Client is being deinit", type: logType) DefaultSocketLogger.Logger.log("Client is being deinit", type: logType)
engine?.close() engine?.close("Client Deinit")
} }
private func addEngine() -> SocketEngineSpec { private func addEngine() -> SocketEngineSpec {
@ -125,16 +125,9 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable
reconnectTimer = nil reconnectTimer = nil
} }
/** @available(*, deprecated=6.0)
Closes the socket. Only reopen the same socket if you know what you're doing.
Will turn off automatic reconnects.
Pass true to fast if you're closing from a background task
*/
public func close() { public func close() {
DefaultSocketLogger.Logger.log("Closing socket", type: logType) disconnect()
reconnects = false
didDisconnect("Closed")
} }
/** /**
@ -171,7 +164,7 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable
dispatch_after(time, handleQueue) {[weak self] in dispatch_after(time, handleQueue) {[weak self] in
if let this = self where this.status != .Connected { if let this = self where this.status != .Connected {
this.status = .Closed this.status = .Closed
this.engine?.close() this.engine?.close("Connect timeout")
handler?() handler?()
} }
@ -222,23 +215,19 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable
reconnects = false reconnects = false
// Make sure the engine is actually dead. // Make sure the engine is actually dead.
engine?.close() engine?.close("Client closed")
handleEvent("disconnect", data: [reason], isInternalMessage: true) handleEvent("disconnect", data: [reason], isInternalMessage: true)
} }
/// error
public func didError(reason: AnyObject) {
DefaultSocketLogger.Logger.error("%@", type: logType, args: reason)
handleEvent("error", data: reason as? [AnyObject] ?? [reason],
isInternalMessage: true)
}
/** /**
Same as close Closes the socket. Only reopen the same socket if you know what you're doing.
Will turn off automatic reconnects.
*/ */
public func disconnect() { public func disconnect() {
close() DefaultSocketLogger.Logger.log("Closing socket", type: logType)
reconnects = false
didDisconnect("Closed")
} }
/** /**
@ -317,6 +306,13 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable
} }
} }
/// error
public func engineDidError(reason: String) {
DefaultSocketLogger.Logger.error("%@", type: logType, args: reason)
handleEvent("error", data: [reason], isInternalMessage: true)
}
// Called when the socket gets an ack for something it sent // Called when the socket gets an ack for something it sent
func handleAck(ack: Int, data: [AnyObject]) { func handleAck(ack: Int, data: [AnyObject]) {
guard status == .Connected else {return} guard status == .Connected else {return}

View File

@ -58,7 +58,7 @@ extension SocketParsable {
case .Disconnect: case .Disconnect:
didDisconnect("Got Disconnect") didDisconnect("Got Disconnect")
case .Error: case .Error:
didError(pack.data) handleEvent("error", data: pack.data, isInternalMessage: false, withAck: pack.id)
default: default:
DefaultSocketLogger.Logger.log("Got invalid packet: %@", type: "SocketParser", args: pack.description) DefaultSocketLogger.Logger.log("Got invalid packet: %@", type: "SocketParser", args: pack.description)
} }