Add SocketManager for multiplexing namespaces
This commit is contained in:
parent
1ed87e571d
commit
5b52edd880
18
CHANGELOG.md
Normal file
18
CHANGELOG.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# v13.0.0
|
||||||
|
|
||||||
|
What's new:
|
||||||
|
---
|
||||||
|
|
||||||
|
-Adds a new `SocketManager` class that multiplexes multiple namespaces through a single engine.
|
||||||
|
- Adds `.sentPing` and `.gotPong` client events for tracking ping/pongs.
|
||||||
|
- watchOS support.
|
||||||
|
|
||||||
|
Important API changes
|
||||||
|
---
|
||||||
|
|
||||||
|
- Many properties that were previously on `SocketIOClient` have been moved to the `SocketManager`.
|
||||||
|
- `SocketIOClientOption.nsp` has been removed. Use `SocketManager.socket(forNamespace:)` to create/get a socket attached to a specific namespace.
|
||||||
|
- Adds `.sentPing` and `.gotPong` client events for tracking ping/pongs.
|
||||||
|
- Makes the framework a single target.
|
||||||
|
- Updates Starscream to 3.0
|
||||||
|
|
||||||
12
README.md
12
README.md
@ -7,21 +7,22 @@ Socket.IO-client for iOS/OS X.
|
|||||||
```swift
|
```swift
|
||||||
import SocketIO
|
import SocketIO
|
||||||
|
|
||||||
let socket = SocketIOClient(socketURL: URL(string: "http://localhost:8080")!, config: [.log(true), .compress])
|
let manager = SocketManager(socketURL: URL(string: "http://localhost:8080")!, config: [.log(true), .compress])
|
||||||
|
let socket = manager.defaultSocket
|
||||||
|
|
||||||
socket.on(clientEvent: .connect) {data, ack in
|
socket.on(clientEvent: .connect) {data, ack in
|
||||||
print("socket connected")
|
print("socket connected")
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.on("currentAmount") {data, ack in
|
socket.on("currentAmount") {data, ack in
|
||||||
if let cur = data[0] as? Double {
|
guard let cur = data[0] as? Double else { return }
|
||||||
|
|
||||||
socket.emitWithAck("canUpdate", cur).timingOut(after: 0) {data in
|
socket.emitWithAck("canUpdate", cur).timingOut(after: 0) {data in
|
||||||
socket.emit("update", ["amount": cur + 2.50])
|
socket.emit("update", ["amount": cur + 2.50])
|
||||||
}
|
}
|
||||||
|
|
||||||
ack.with("Got your currentAmount", "dude")
|
ack.with("Got your currentAmount", "dude")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
socket.connect()
|
socket.connect()
|
||||||
```
|
```
|
||||||
@ -29,8 +30,10 @@ socket.connect()
|
|||||||
## Objective-C Example
|
## Objective-C Example
|
||||||
```objective-c
|
```objective-c
|
||||||
@import SocketIO;
|
@import SocketIO;
|
||||||
|
|
||||||
NSURL* url = [[NSURL alloc] initWithString:@"http://localhost:8080"];
|
NSURL* url = [[NSURL alloc] initWithString:@"http://localhost:8080"];
|
||||||
SocketIOClient* socket = [[SocketIOClient alloc] initWithSocketURL:url config:@{@"log": @YES, @"compress": @YES}];
|
SocketManager* manager = [[SocketManager alloc] initWithSocketURL:url config:@{@"log": @YES, @"compress": @YES}];
|
||||||
|
SocketIOClient* socket = manager.defaultSocket;
|
||||||
|
|
||||||
[socket on:@"connect" callback:^(NSArray* data, SocketAckEmitter* ack) {
|
[socket on:@"connect" callback:^(NSArray* data, SocketAckEmitter* ack) {
|
||||||
NSLog(@"socket connected");
|
NSLog(@"socket connected");
|
||||||
@ -134,6 +137,7 @@ Objective-C:
|
|||||||
# [Docs](https://nuclearace.github.io/Socket.IO-Client-Swift/index.html)
|
# [Docs](https://nuclearace.github.io/Socket.IO-Client-Swift/index.html)
|
||||||
|
|
||||||
- [Client](https://nuclearace.github.io/Socket.IO-Client-Swift/Classes/SocketIOClient.html)
|
- [Client](https://nuclearace.github.io/Socket.IO-Client-Swift/Classes/SocketIOClient.html)
|
||||||
|
- [Manager](https://nuclearace.github.io/Socket.IO-Client-Swift/Classes/SocketManager.html)
|
||||||
- [Engine](https://nuclearace.github.io/Socket.IO-Client-Swift/Classes/SocketEngine.html)
|
- [Engine](https://nuclearace.github.io/Socket.IO-Client-Swift/Classes/SocketEngine.html)
|
||||||
- [Options](https://nuclearace.github.io/Socket.IO-Client-Swift/Enums/SocketIOClientOption.html)
|
- [Options](https://nuclearace.github.io/Socket.IO-Client-Swift/Enums/SocketIOClientOption.html)
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,8 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
1C6572803D7E252A77A86E5F /* SocketManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C65763817782DFAC67BE05C /* SocketManager.swift */; };
|
||||||
|
1C657FBB3F670261780FD72E /* SocketManagerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6574AF9687A213814753E4 /* SocketManagerSpec.swift */; };
|
||||||
1C686BE21F869AFD007D8627 /* SocketIOClientConfigurationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C686BD21F869AF1007D8627 /* SocketIOClientConfigurationTest.swift */; };
|
1C686BE21F869AFD007D8627 /* SocketIOClientConfigurationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C686BD21F869AF1007D8627 /* SocketIOClientConfigurationTest.swift */; };
|
||||||
1C686BE31F869AFD007D8627 /* SocketEngineTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C686BD31F869AF1007D8627 /* SocketEngineTest.swift */; };
|
1C686BE31F869AFD007D8627 /* SocketEngineTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C686BD31F869AF1007D8627 /* SocketEngineTest.swift */; };
|
||||||
1C686BE41F869AFD007D8627 /* SocketSideEffectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C686BD41F869AF1007D8627 /* SocketSideEffectTest.swift */; };
|
1C686BE41F869AFD007D8627 /* SocketSideEffectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C686BD41F869AF1007D8627 /* SocketSideEffectTest.swift */; };
|
||||||
@ -30,17 +32,18 @@
|
|||||||
DD52B3A6C1E082841C35C85D /* SocketEngineClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52BE5FDCE1D684132E897C /* SocketEngineClient.swift */; };
|
DD52B3A6C1E082841C35C85D /* SocketEngineClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52BE5FDCE1D684132E897C /* SocketEngineClient.swift */; };
|
||||||
DD52B44AE56F2E07F3F3F991 /* SocketAckManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B09F7984E730513AB7E5 /* SocketAckManager.swift */; };
|
DD52B44AE56F2E07F3F3F991 /* SocketAckManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B09F7984E730513AB7E5 /* SocketAckManager.swift */; };
|
||||||
DD52B4DFA12F2599410205D9 /* SocketEngineWebsocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52BE9AD8B2BD7F841CD1D4 /* SocketEngineWebsocket.swift */; };
|
DD52B4DFA12F2599410205D9 /* SocketEngineWebsocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52BE9AD8B2BD7F841CD1D4 /* SocketEngineWebsocket.swift */; };
|
||||||
|
DD52B53F2609D91A683DFCDD /* ManagerObjectiveCTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DD52BB5E907D283ACC31E17F /* ManagerObjectiveCTest.m */; };
|
||||||
DD52B56DE03CDB4F40BD1A23 /* SocketExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B471D780013E18DF9335 /* SocketExtensions.swift */; };
|
DD52B56DE03CDB4F40BD1A23 /* SocketExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B471D780013E18DF9335 /* SocketExtensions.swift */; };
|
||||||
DD52B57E7ABC61B57EE2A4B8 /* SocketPacket.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B59C11D3D2BC63612E50 /* SocketPacket.swift */; };
|
DD52B57E7ABC61B57EE2A4B8 /* SocketPacket.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B59C11D3D2BC63612E50 /* SocketPacket.swift */; };
|
||||||
DD52B660D63B6A25C3755AA7 /* SocketClientManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B282975446C9A9C56D7B /* SocketClientManager.swift */; };
|
|
||||||
DD52B883F942CD5A9D29892B /* SocketEnginePollable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B2D110F55723F82B108E /* SocketEnginePollable.swift */; };
|
DD52B883F942CD5A9D29892B /* SocketEnginePollable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B2D110F55723F82B108E /* SocketEnginePollable.swift */; };
|
||||||
DD52B9412F660F828B683422 /* SocketParsable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B31D0E6815F5F10CEFB6 /* SocketParsable.swift */; };
|
DD52B9412F660F828B683422 /* SocketParsable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B31D0E6815F5F10CEFB6 /* SocketParsable.swift */; };
|
||||||
DD52BB69B6D260035B652CA4 /* SocketAnyEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B5A9DE10C7A8AD35617F /* SocketAnyEvent.swift */; };
|
DD52BB69B6D260035B652CA4 /* SocketAnyEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B5A9DE10C7A8AD35617F /* SocketAnyEvent.swift */; };
|
||||||
DD52BB82239886CF6ADD642C /* SocketEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B7A9779A2E08075E5AAC /* SocketEngine.swift */; };
|
DD52BB82239886CF6ADD642C /* SocketEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B7A9779A2E08075E5AAC /* SocketEngine.swift */; };
|
||||||
DD52BB9A3E42FF2DD6BE7C2F /* SocketIOClientSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52BCAF915A546288664346 /* SocketIOClientSpec.swift */; };
|
DD52BB9A3E42FF2DD6BE7C2F /* SocketIOClientSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52BCAF915A546288664346 /* SocketIOClientSpec.swift */; };
|
||||||
DD52BC3F1F880820E8FDFD0C /* SocketLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52BED81BF312B0E90E92AC /* SocketLogger.swift */; };
|
DD52BC3F1F880820E8FDFD0C /* SocketLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52BED81BF312B0E90E92AC /* SocketLogger.swift */; };
|
||||||
|
DD52BCCD25EFA76E0F9B313C /* SocketMangerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52BBAC5FAA7730D32CD5BF /* SocketMangerTest.swift */; };
|
||||||
DD52BD065B74AC5B77BAEFAA /* SocketIOClientConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B57FFEE8560CFFD793B3 /* SocketIOClientConfiguration.swift */; };
|
DD52BD065B74AC5B77BAEFAA /* SocketIOClientConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B57FFEE8560CFFD793B3 /* SocketIOClientConfiguration.swift */; };
|
||||||
DD52BE4D1E6BB752CD9614A6 /* SocketIOClientStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B1D9BC4AE46D38D827DE /* SocketIOClientStatus.swift */; };
|
DD52BE4D1E6BB752CD9614A6 /* SocketIOStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B1D9BC4AE46D38D827DE /* SocketIOStatus.swift */; };
|
||||||
DD52BF924BEF05E1235CFD29 /* SocketIOClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52BA1F41F2E4B3DC20260E /* SocketIOClient.swift */; };
|
DD52BF924BEF05E1235CFD29 /* SocketIOClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52BA1F41F2E4B3DC20260E /* SocketIOClient.swift */; };
|
||||||
DD52BFBC9E7CC32D3515AC80 /* SocketEngineSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B645273A873667BC2D43 /* SocketEngineSpec.swift */; };
|
DD52BFBC9E7CC32D3515AC80 /* SocketEngineSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B645273A873667BC2D43 /* SocketEngineSpec.swift */; };
|
||||||
DD52BFEB4DBD3BF8D93DAEFF /* SocketEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B6DCCBBAC6BE9C22568D /* SocketEventHandler.swift */; };
|
DD52BFEB4DBD3BF8D93DAEFF /* SocketEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B6DCCBBAC6BE9C22568D /* SocketEventHandler.swift */; };
|
||||||
@ -57,6 +60,8 @@
|
|||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
1C6574AF9687A213814753E4 /* SocketManagerSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketManagerSpec.swift; sourceTree = "<group>"; };
|
||||||
|
1C65763817782DFAC67BE05C /* SocketManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketManager.swift; sourceTree = "<group>"; };
|
||||||
1C686BD21F869AF1007D8627 /* SocketIOClientConfigurationTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketIOClientConfigurationTest.swift; sourceTree = "<group>"; };
|
1C686BD21F869AF1007D8627 /* SocketIOClientConfigurationTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketIOClientConfigurationTest.swift; sourceTree = "<group>"; };
|
||||||
1C686BD31F869AF1007D8627 /* SocketEngineTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketEngineTest.swift; sourceTree = "<group>"; };
|
1C686BD31F869AF1007D8627 /* SocketEngineTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketEngineTest.swift; sourceTree = "<group>"; };
|
||||||
1C686BD41F869AF1007D8627 /* SocketSideEffectTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketSideEffectTest.swift; sourceTree = "<group>"; };
|
1C686BD41F869AF1007D8627 /* SocketSideEffectTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketSideEffectTest.swift; sourceTree = "<group>"; };
|
||||||
@ -83,8 +88,8 @@
|
|||||||
9432E00D1F77F889006AF628 /* Starscream.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Starscream.framework; path = Carthage/Build/tvOS/Starscream.framework; sourceTree = "<group>"; };
|
9432E00D1F77F889006AF628 /* Starscream.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Starscream.framework; path = Carthage/Build/tvOS/Starscream.framework; sourceTree = "<group>"; };
|
||||||
DD52B078DB0A3C3D1BB507CD /* SocketIOClientOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketIOClientOption.swift; sourceTree = "<group>"; };
|
DD52B078DB0A3C3D1BB507CD /* SocketIOClientOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketIOClientOption.swift; sourceTree = "<group>"; };
|
||||||
DD52B09F7984E730513AB7E5 /* SocketAckManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketAckManager.swift; sourceTree = "<group>"; };
|
DD52B09F7984E730513AB7E5 /* SocketAckManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketAckManager.swift; sourceTree = "<group>"; };
|
||||||
DD52B1D9BC4AE46D38D827DE /* SocketIOClientStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketIOClientStatus.swift; sourceTree = "<group>"; };
|
DD52B1D9BC4AE46D38D827DE /* SocketIOStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketIOStatus.swift; sourceTree = "<group>"; };
|
||||||
DD52B282975446C9A9C56D7B /* SocketClientManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketClientManager.swift; sourceTree = "<group>"; };
|
DD52B2C54A6ADF3371C13DCB /* SocketObjectiveCTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SocketObjectiveCTest.h; sourceTree = "<group>"; };
|
||||||
DD52B2D110F55723F82B108E /* SocketEnginePollable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEnginePollable.swift; sourceTree = "<group>"; };
|
DD52B2D110F55723F82B108E /* SocketEnginePollable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEnginePollable.swift; sourceTree = "<group>"; };
|
||||||
DD52B31D0E6815F5F10CEFB6 /* SocketParsable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketParsable.swift; sourceTree = "<group>"; };
|
DD52B31D0E6815F5F10CEFB6 /* SocketParsable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketParsable.swift; sourceTree = "<group>"; };
|
||||||
DD52B471D780013E18DF9335 /* SocketExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketExtensions.swift; sourceTree = "<group>"; };
|
DD52B471D780013E18DF9335 /* SocketExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketExtensions.swift; sourceTree = "<group>"; };
|
||||||
@ -95,8 +100,11 @@
|
|||||||
DD52B645273A873667BC2D43 /* SocketEngineSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEngineSpec.swift; sourceTree = "<group>"; };
|
DD52B645273A873667BC2D43 /* SocketEngineSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEngineSpec.swift; sourceTree = "<group>"; };
|
||||||
DD52B6DCCBBAC6BE9C22568D /* SocketEventHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEventHandler.swift; sourceTree = "<group>"; };
|
DD52B6DCCBBAC6BE9C22568D /* SocketEventHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEventHandler.swift; sourceTree = "<group>"; };
|
||||||
DD52B7A9779A2E08075E5AAC /* SocketEngine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEngine.swift; sourceTree = "<group>"; };
|
DD52B7A9779A2E08075E5AAC /* SocketEngine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEngine.swift; sourceTree = "<group>"; };
|
||||||
|
DD52B8396C7DEE7BFD6A985A /* ManagerObjectiveCTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ManagerObjectiveCTest.h; sourceTree = "<group>"; };
|
||||||
DD52BA1F41F2E4B3DC20260E /* SocketIOClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketIOClient.swift; sourceTree = "<group>"; };
|
DD52BA1F41F2E4B3DC20260E /* SocketIOClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketIOClient.swift; sourceTree = "<group>"; };
|
||||||
DD52BA240D139F72633D4159 /* SocketStringReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketStringReader.swift; sourceTree = "<group>"; };
|
DD52BA240D139F72633D4159 /* SocketStringReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketStringReader.swift; sourceTree = "<group>"; };
|
||||||
|
DD52BB5E907D283ACC31E17F /* ManagerObjectiveCTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ManagerObjectiveCTest.m; sourceTree = "<group>"; };
|
||||||
|
DD52BBAC5FAA7730D32CD5BF /* SocketMangerTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketMangerTest.swift; sourceTree = "<group>"; };
|
||||||
DD52BCAF915A546288664346 /* SocketIOClientSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketIOClientSpec.swift; sourceTree = "<group>"; };
|
DD52BCAF915A546288664346 /* SocketIOClientSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketIOClientSpec.swift; sourceTree = "<group>"; };
|
||||||
DD52BDC9E66AADA2CC5E8246 /* SocketTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketTypes.swift; sourceTree = "<group>"; };
|
DD52BDC9E66AADA2CC5E8246 /* SocketTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketTypes.swift; sourceTree = "<group>"; };
|
||||||
DD52BE5FDCE1D684132E897C /* SocketEngineClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEngineClient.swift; sourceTree = "<group>"; };
|
DD52BE5FDCE1D684132E897C /* SocketEngineClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEngineClient.swift; sourceTree = "<group>"; };
|
||||||
@ -128,6 +136,16 @@
|
|||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
1C657951DEA2E0293D0FD1B6 /* Manager */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
1C65763817782DFAC67BE05C /* SocketManager.swift */,
|
||||||
|
1C6574AF9687A213814753E4 /* SocketManagerSpec.swift */,
|
||||||
|
);
|
||||||
|
name = Manager;
|
||||||
|
path = Source/SocketIO/Manager;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
1C686BD11F869AF1007D8627 /* TestSocketIO */ = {
|
1C686BD11F869AF1007D8627 /* TestSocketIO */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -138,6 +156,7 @@
|
|||||||
1C686BD61F869AF1007D8627 /* SocketAckManagerTest.swift */,
|
1C686BD61F869AF1007D8627 /* SocketAckManagerTest.swift */,
|
||||||
1C686BD71F869AF1007D8627 /* SocketParserTest.swift */,
|
1C686BD71F869AF1007D8627 /* SocketParserTest.swift */,
|
||||||
1C686BD81F869AF1007D8627 /* SocketNamespacePacketTest.swift */,
|
1C686BD81F869AF1007D8627 /* SocketNamespacePacketTest.swift */,
|
||||||
|
DD52BBAC5FAA7730D32CD5BF /* SocketMangerTest.swift */,
|
||||||
);
|
);
|
||||||
name = TestSocketIO;
|
name = TestSocketIO;
|
||||||
path = Tests/TestSocketIO;
|
path = Tests/TestSocketIO;
|
||||||
@ -147,6 +166,9 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
1C686BFE1F869E9D007D8627 /* SocketObjectiveCTest.m */,
|
1C686BFE1F869E9D007D8627 /* SocketObjectiveCTest.m */,
|
||||||
|
DD52BB5E907D283ACC31E17F /* ManagerObjectiveCTest.m */,
|
||||||
|
DD52B8396C7DEE7BFD6A985A /* ManagerObjectiveCTest.h */,
|
||||||
|
DD52B2C54A6ADF3371C13DCB /* SocketObjectiveCTest.h */,
|
||||||
);
|
);
|
||||||
name = TestSocketIOObjc;
|
name = TestSocketIOObjc;
|
||||||
path = Tests/TestSocketIOObjc;
|
path = Tests/TestSocketIOObjc;
|
||||||
@ -198,6 +220,7 @@
|
|||||||
DD52B6A0966AF71393777311 /* Client */,
|
DD52B6A0966AF71393777311 /* Client */,
|
||||||
DD52B1D10D761CEF3944A6BC /* Util */,
|
DD52B1D10D761CEF3944A6BC /* Util */,
|
||||||
DD52B647ED881F3FF6EEC617 /* Parse */,
|
DD52B647ED881F3FF6EEC617 /* Parse */,
|
||||||
|
1C657951DEA2E0293D0FD1B6 /* Manager */,
|
||||||
);
|
);
|
||||||
name = Source;
|
name = Source;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -251,7 +274,6 @@
|
|||||||
DD52BED81BF312B0E90E92AC /* SocketLogger.swift */,
|
DD52BED81BF312B0E90E92AC /* SocketLogger.swift */,
|
||||||
DD52B471D780013E18DF9335 /* SocketExtensions.swift */,
|
DD52B471D780013E18DF9335 /* SocketExtensions.swift */,
|
||||||
DD52BA240D139F72633D4159 /* SocketStringReader.swift */,
|
DD52BA240D139F72633D4159 /* SocketStringReader.swift */,
|
||||||
DD52B282975446C9A9C56D7B /* SocketClientManager.swift */,
|
|
||||||
9432E0061F77F7CA006AF628 /* SSLSecurity.swift */,
|
9432E0061F77F7CA006AF628 /* SSLSecurity.swift */,
|
||||||
);
|
);
|
||||||
name = Util;
|
name = Util;
|
||||||
@ -276,7 +298,7 @@
|
|||||||
DD52B6DCCBBAC6BE9C22568D /* SocketEventHandler.swift */,
|
DD52B6DCCBBAC6BE9C22568D /* SocketEventHandler.swift */,
|
||||||
DD52BCAF915A546288664346 /* SocketIOClientSpec.swift */,
|
DD52BCAF915A546288664346 /* SocketIOClientSpec.swift */,
|
||||||
DD52B078DB0A3C3D1BB507CD /* SocketIOClientOption.swift */,
|
DD52B078DB0A3C3D1BB507CD /* SocketIOClientOption.swift */,
|
||||||
DD52B1D9BC4AE46D38D827DE /* SocketIOClientStatus.swift */,
|
DD52B1D9BC4AE46D38D827DE /* SocketIOStatus.swift */,
|
||||||
DD52B57FFEE8560CFFD793B3 /* SocketIOClientConfiguration.swift */,
|
DD52B57FFEE8560CFFD793B3 /* SocketIOClientConfiguration.swift */,
|
||||||
);
|
);
|
||||||
name = Client;
|
name = Client;
|
||||||
@ -448,15 +470,16 @@
|
|||||||
9432E00F1F77F8C4006AF628 /* SSLSecurity.swift in Sources */,
|
9432E00F1F77F8C4006AF628 /* SSLSecurity.swift in Sources */,
|
||||||
DD52BB9A3E42FF2DD6BE7C2F /* SocketIOClientSpec.swift in Sources */,
|
DD52BB9A3E42FF2DD6BE7C2F /* SocketIOClientSpec.swift in Sources */,
|
||||||
DD52B2AFE7D46039C7AE4D19 /* SocketIOClientOption.swift in Sources */,
|
DD52B2AFE7D46039C7AE4D19 /* SocketIOClientOption.swift in Sources */,
|
||||||
DD52BE4D1E6BB752CD9614A6 /* SocketIOClientStatus.swift in Sources */,
|
DD52BE4D1E6BB752CD9614A6 /* SocketIOStatus.swift in Sources */,
|
||||||
DD52BD065B74AC5B77BAEFAA /* SocketIOClientConfiguration.swift in Sources */,
|
DD52BD065B74AC5B77BAEFAA /* SocketIOClientConfiguration.swift in Sources */,
|
||||||
DD52B048C71D724ABBD18C71 /* SocketTypes.swift in Sources */,
|
DD52B048C71D724ABBD18C71 /* SocketTypes.swift in Sources */,
|
||||||
DD52BC3F1F880820E8FDFD0C /* SocketLogger.swift in Sources */,
|
DD52BC3F1F880820E8FDFD0C /* SocketLogger.swift in Sources */,
|
||||||
DD52B56DE03CDB4F40BD1A23 /* SocketExtensions.swift in Sources */,
|
DD52B56DE03CDB4F40BD1A23 /* SocketExtensions.swift in Sources */,
|
||||||
DD52B11AF936352BAE30B2C8 /* SocketStringReader.swift in Sources */,
|
DD52B11AF936352BAE30B2C8 /* SocketStringReader.swift in Sources */,
|
||||||
DD52B660D63B6A25C3755AA7 /* SocketClientManager.swift in Sources */,
|
|
||||||
DD52B57E7ABC61B57EE2A4B8 /* SocketPacket.swift in Sources */,
|
DD52B57E7ABC61B57EE2A4B8 /* SocketPacket.swift in Sources */,
|
||||||
DD52B9412F660F828B683422 /* SocketParsable.swift in Sources */,
|
DD52B9412F660F828B683422 /* SocketParsable.swift in Sources */,
|
||||||
|
1C6572803D7E252A77A86E5F /* SocketManager.swift in Sources */,
|
||||||
|
1C657FBB3F670261780FD72E /* SocketManagerSpec.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -472,6 +495,8 @@
|
|||||||
1C686BE61F869AFD007D8627 /* SocketAckManagerTest.swift in Sources */,
|
1C686BE61F869AFD007D8627 /* SocketAckManagerTest.swift in Sources */,
|
||||||
1C686BE71F869AFD007D8627 /* SocketParserTest.swift in Sources */,
|
1C686BE71F869AFD007D8627 /* SocketParserTest.swift in Sources */,
|
||||||
1C686BE81F869AFD007D8627 /* SocketNamespacePacketTest.swift in Sources */,
|
1C686BE81F869AFD007D8627 /* SocketNamespacePacketTest.swift in Sources */,
|
||||||
|
DD52BCCD25EFA76E0F9B313C /* SocketMangerTest.swift in Sources */,
|
||||||
|
DD52B53F2609D91A683DFCDD /* ManagerObjectiveCTest.m in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -120,8 +120,10 @@ public final class OnAckCallback : NSObject {
|
|||||||
|
|
||||||
guard seconds != 0 else { return }
|
guard seconds != 0 else { return }
|
||||||
|
|
||||||
socket.handleQueue.asyncAfter(deadline: DispatchTime.now() + seconds) {
|
socket.manager?.handleQueue.asyncAfter(deadline: DispatchTime.now() + seconds) {[weak socket] in
|
||||||
socket.ackHandlers.timeoutAck(self.ackNumber, onQueue: socket.handleQueue)
|
guard let socket = socket, let manager = socket.manager else { return }
|
||||||
|
|
||||||
|
socket.ackHandlers.timeoutAck(self.ackNumber, onQueue: manager.handleQueue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -25,110 +25,52 @@
|
|||||||
import Dispatch
|
import Dispatch
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/// The main class for SocketIOClientSwift.
|
/// Represents a socket.io-client.
|
||||||
///
|
///
|
||||||
/// **NOTE**: The client is not thread/queue safe, all interaction with the socket should be done on the `handleQueue`
|
/// Clients are created through a `SocketManager`, which owns the `SocketEngineSpec` that controls the connection to the server.
|
||||||
///
|
///
|
||||||
/// Represents a socket.io-client. Most interaction with socket.io will be through this class.
|
/// For example:
|
||||||
open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, SocketParsable {
|
///
|
||||||
|
/// ```swift
|
||||||
|
/// // Create a socket for the /swift namespace
|
||||||
|
/// let socket = manager.socket(forNamespace: "/swift")
|
||||||
|
///
|
||||||
|
/// // Add some handlers and connect
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// **NOTE**: The client is not thread/queue safe, all interaction with the socket should be done on the `manager.handleQueue`
|
||||||
|
///
|
||||||
|
open class SocketIOClient : NSObject, SocketIOClientSpec {
|
||||||
// MARK: Properties
|
// MARK: Properties
|
||||||
|
|
||||||
private static let logType = "SocketIOClient"
|
|
||||||
|
|
||||||
/// If `true` then every time `connect` is called, a new engine will be created.
|
|
||||||
@objc
|
|
||||||
public var forceNew = false
|
|
||||||
|
|
||||||
/// The queue that all interaction with the client should occur on. This is the queue that event handlers are
|
|
||||||
/// called on.
|
|
||||||
///
|
|
||||||
/// **This should be a serial queue! Concurrent queues are not supported and might cause crashes and races**.
|
|
||||||
@objc
|
|
||||||
public var handleQueue = DispatchQueue.main
|
|
||||||
|
|
||||||
/// The namespace that this socket is currently connected to.
|
/// The namespace that this socket is currently connected to.
|
||||||
///
|
///
|
||||||
/// **Must** start with a `/`.
|
/// **Must** start with a `/`.
|
||||||
@objc
|
@objc
|
||||||
public var nsp = "/"
|
public var nsp = "/"
|
||||||
|
|
||||||
/// The configuration for this client.
|
|
||||||
///
|
|
||||||
/// **This cannot be set after calling one of the connect methods**.
|
|
||||||
public var config: SocketIOClientConfiguration {
|
|
||||||
get {
|
|
||||||
return _config
|
|
||||||
}
|
|
||||||
|
|
||||||
set {
|
|
||||||
guard status == .notConnected else {
|
|
||||||
DefaultSocketLogger.Logger.error("Tried setting config after calling connect",
|
|
||||||
type: SocketIOClient.logType)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_config = newValue
|
|
||||||
|
|
||||||
if socketURL.absoluteString.hasPrefix("https://") {
|
|
||||||
_config.insert(.secure(true))
|
|
||||||
}
|
|
||||||
|
|
||||||
_config.insert(.path("/socket.io/"), replacing: false)
|
|
||||||
setConfigs()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If `true`, this client will try and reconnect on any disconnects.
|
|
||||||
@objc
|
|
||||||
public var reconnects = true
|
|
||||||
|
|
||||||
/// The number of seconds to wait before attempting to reconnect.
|
|
||||||
@objc
|
|
||||||
public var reconnectWait = 10
|
|
||||||
|
|
||||||
/// The session id of this client.
|
/// The session id of this client.
|
||||||
@objc
|
@objc
|
||||||
public var sid: String? {
|
public var sid: String {
|
||||||
return engine?.sid
|
guard let engine = manager?.engine else { return "" }
|
||||||
|
|
||||||
|
return nsp == "/" ? engine.sid : "\(nsp)#\(engine.sid)"
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The URL of the socket.io server.
|
|
||||||
///
|
|
||||||
/// If changed after calling `init`, `forceNew` must be set to `true`, or it will only connect to the url set in the
|
|
||||||
/// init.
|
|
||||||
@objc
|
|
||||||
public var socketURL: URL
|
|
||||||
|
|
||||||
/// A list of packets that are waiting for binary data.
|
|
||||||
///
|
|
||||||
/// The way that socket.io works all data should be sent directly after each packet.
|
|
||||||
/// So this should ideally be an array of one packet waiting for data.
|
|
||||||
///
|
|
||||||
/// **This should not be modified directly.**
|
|
||||||
public var waitingPackets = [SocketPacket]()
|
|
||||||
|
|
||||||
/// A handler that will be called on any event.
|
/// A handler that will be called on any event.
|
||||||
public private(set) var anyHandler: ((SocketAnyEvent) -> ())?
|
public private(set) var anyHandler: ((SocketAnyEvent) -> ())?
|
||||||
|
|
||||||
/// The engine for this client.
|
|
||||||
@objc
|
|
||||||
public internal(set) var engine: SocketEngineSpec?
|
|
||||||
|
|
||||||
/// The array of handlers for this socket.
|
/// The array of handlers for this socket.
|
||||||
public private(set) var handlers = [SocketEventHandler]()
|
public private(set) var handlers = [SocketEventHandler]()
|
||||||
|
|
||||||
|
/// The manager for this socket.
|
||||||
|
@objc
|
||||||
|
public private(set) weak var manager: SocketManagerSpec?
|
||||||
|
|
||||||
/// The status of this client.
|
/// The status of this client.
|
||||||
@objc
|
@objc
|
||||||
public private(set) var status = SocketIOClientStatus.notConnected {
|
public private(set) var status = SocketIOStatus.notConnected {
|
||||||
didSet {
|
didSet {
|
||||||
switch status {
|
|
||||||
case .connected:
|
|
||||||
reconnecting = false
|
|
||||||
currentReconnectAttempt = 0
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
handleClientEvent(.statusChange, data: [status])
|
handleClientEvent(.statusChange, data: [status])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,60 +78,29 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
var ackHandlers = SocketAckManager()
|
var ackHandlers = SocketAckManager()
|
||||||
|
|
||||||
private(set) var currentAck = -1
|
private(set) var currentAck = -1
|
||||||
private(set) var reconnectAttempts = -1
|
|
||||||
|
|
||||||
private var _config: SocketIOClientConfiguration
|
private lazy var logType = "SocketIOClient{\(nsp)}"
|
||||||
private var currentReconnectAttempt = 0
|
|
||||||
private var reconnecting = false
|
|
||||||
|
|
||||||
// MARK: Initializers
|
// MARK: Initializers
|
||||||
|
|
||||||
/// Type safe way to create a new SocketIOClient. `opts` can be omitted.
|
/// Type safe way to create a new SocketIOClient. `opts` can be omitted.
|
||||||
///
|
///
|
||||||
|
/// - parameter manager: The manager for this socket.
|
||||||
/// - parameter socketURL: The url of the socket.io server.
|
/// - parameter socketURL: The url of the socket.io server.
|
||||||
/// - parameter config: The config for this socket.
|
@objc
|
||||||
public init(socketURL: URL, config: SocketIOClientConfiguration = []) {
|
public init(manager: SocketManagerSpec, nsp: String) {
|
||||||
self._config = config
|
self.manager = manager
|
||||||
self.socketURL = socketURL
|
self.nsp = nsp
|
||||||
|
|
||||||
if socketURL.absoluteString.hasPrefix("https://") {
|
|
||||||
self._config.insert(.secure(true))
|
|
||||||
}
|
|
||||||
|
|
||||||
self._config.insert(.path("/socket.io/"), replacing: false)
|
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
setConfigs()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Not so type safe way to create a SocketIOClient, meant for Objective-C compatiblity.
|
|
||||||
/// If using Swift it's recommended to use `init(socketURL: NSURL, options: Set<SocketIOClientOption>)`
|
|
||||||
///
|
|
||||||
/// - parameter socketURL: The url of the socket.io server.
|
|
||||||
/// - parameter config: The config for this socket.
|
|
||||||
@objc
|
|
||||||
public convenience init(socketURL: NSURL, config: NSDictionary?) {
|
|
||||||
self.init(socketURL: socketURL as URL, config: config?.toSocketConfiguration() ?? [])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
DefaultSocketLogger.Logger.log("Client is being released", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Client is being released", type: logType)
|
||||||
engine?.disconnect(reason: "Client Deinit")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Methods
|
// MARK: Methods
|
||||||
|
|
||||||
private func addEngine() {
|
|
||||||
DefaultSocketLogger.Logger.log("Adding engine", type: SocketIOClient.logType)
|
|
||||||
|
|
||||||
engine?.engineQueue.sync {
|
|
||||||
self.engine?.client = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
engine = SocketEngine(client: self, url: socketURL, config: config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Connect to the server. The same as calling `connect(timeoutAfter:withHandler:)` with a timeout of 0.
|
/// Connect to the server. The same as calling `connect(timeoutAfter:withHandler:)` with a timeout of 0.
|
||||||
///
|
///
|
||||||
/// Only call after adding your event listeners, unless you know what you're doing.
|
/// Only call after adding your event listeners, unless you know what you're doing.
|
||||||
@ -209,27 +120,22 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
open func connect(timeoutAfter: Double, withHandler handler: (() -> ())?) {
|
open func connect(timeoutAfter: Double, withHandler handler: (() -> ())?) {
|
||||||
assert(timeoutAfter >= 0, "Invalid timeout: \(timeoutAfter)")
|
assert(timeoutAfter >= 0, "Invalid timeout: \(timeoutAfter)")
|
||||||
|
|
||||||
guard status != .connected else {
|
guard let manager = self.manager, status != .connected else {
|
||||||
DefaultSocketLogger.Logger.log("Tried connecting on an already connected socket",
|
DefaultSocketLogger.Logger.log("Tried connecting on an already connected socket", type: logType)
|
||||||
type: SocketIOClient.logType)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
status = .connecting
|
status = .connecting
|
||||||
|
|
||||||
if engine == nil || forceNew {
|
manager.connectSocket(self)
|
||||||
addEngine()
|
|
||||||
}
|
|
||||||
|
|
||||||
engine?.connect()
|
|
||||||
|
|
||||||
guard timeoutAfter != 0 else { return }
|
guard timeoutAfter != 0 else { return }
|
||||||
|
|
||||||
handleQueue.asyncAfter(deadline: DispatchTime.now() + timeoutAfter) {[weak self] in
|
manager.handleQueue.asyncAfter(deadline: DispatchTime.now() + timeoutAfter) {[weak self] in
|
||||||
guard let this = self, this.status == .connecting || this.status == .notConnected else { return }
|
guard let this = self, this.status == .connecting || this.status == .notConnected else { return }
|
||||||
|
|
||||||
this.status = .disconnected
|
this.status = .disconnected
|
||||||
this.engine?.disconnect(reason: "Connect timeout")
|
this.leaveNamespace()
|
||||||
|
|
||||||
handler?()
|
handler?()
|
||||||
}
|
}
|
||||||
@ -248,7 +154,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
open func didConnect(toNamespace namespace: String) {
|
open func didConnect(toNamespace namespace: String) {
|
||||||
guard status != .connected else { return }
|
guard status != .connected else { return }
|
||||||
|
|
||||||
DefaultSocketLogger.Logger.log("Socket connected", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Socket connected", type: logType)
|
||||||
|
|
||||||
status = .connected
|
status = .connected
|
||||||
|
|
||||||
@ -261,21 +167,22 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
open func didDisconnect(reason: String) {
|
open func didDisconnect(reason: String) {
|
||||||
guard status != .disconnected else { return }
|
guard status != .disconnected else { return }
|
||||||
|
|
||||||
DefaultSocketLogger.Logger.log("Disconnected: \(reason)", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Disconnected: \(reason)", type: logType)
|
||||||
|
|
||||||
reconnecting = false
|
|
||||||
status = .disconnected
|
status = .disconnected
|
||||||
|
|
||||||
// Make sure the engine is actually dead.
|
|
||||||
engine?.disconnect(reason: reason)
|
|
||||||
handleClientEvent(.disconnect, data: [reason])
|
handleClientEvent(.disconnect, data: [reason])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Disconnects the socket.
|
/// Disconnects the socket.
|
||||||
|
///
|
||||||
|
/// This will cause the socket to leave the namespace it is associated to, as well as remove itself from the
|
||||||
|
/// `manager`.
|
||||||
@objc
|
@objc
|
||||||
open func disconnect() {
|
open func disconnect() {
|
||||||
DefaultSocketLogger.Logger.log("Closing socket", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Closing socket", type: logType)
|
||||||
|
|
||||||
|
leaveNamespace()
|
||||||
didDisconnect(reason: "Disconnect")
|
didDisconnect(reason: "Disconnect")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,7 +198,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
try emit(event, with: items.map({ try $0.socketRepresentation() }))
|
try emit(event, with: items.map({ try $0.socketRepresentation() }))
|
||||||
} catch let err {
|
} catch let err {
|
||||||
DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)",
|
DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)",
|
||||||
type: SocketIOClient.logType)
|
type: logType)
|
||||||
|
|
||||||
handleClientEvent(.error, data: [event, items, err])
|
handleClientEvent(.error, data: [event, items, err])
|
||||||
}
|
}
|
||||||
@ -335,7 +242,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
return emitWithAck(event, with: try items.map({ try $0.socketRepresentation() }))
|
return emitWithAck(event, with: try items.map({ try $0.socketRepresentation() }))
|
||||||
} catch let err {
|
} catch let err {
|
||||||
DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)",
|
DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)",
|
||||||
type: SocketIOClient.logType)
|
type: logType)
|
||||||
|
|
||||||
handleClientEvent(.error, data: [event, items, err])
|
handleClientEvent(.error, data: [event, items, err])
|
||||||
|
|
||||||
@ -373,9 +280,9 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
let packet = SocketPacket.packetFromEmit(data, id: ack ?? -1, nsp: nsp, ack: false)
|
let packet = SocketPacket.packetFromEmit(data, id: ack ?? -1, nsp: nsp, ack: false)
|
||||||
let str = packet.packetString
|
let str = packet.packetString
|
||||||
|
|
||||||
DefaultSocketLogger.Logger.log("Emitting: \(str)", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Emitting: \(str)", type: logType)
|
||||||
|
|
||||||
engine?.send(str, withData: packet.binary)
|
manager?.engine?.send(str, withData: packet.binary)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call when you wish to tell the server that you've received the event for `ack`.
|
/// Call when you wish to tell the server that you've received the event for `ack`.
|
||||||
@ -390,91 +297,9 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
let packet = SocketPacket.packetFromEmit(items, id: ack, nsp: nsp, ack: true)
|
let packet = SocketPacket.packetFromEmit(items, id: ack, nsp: nsp, ack: true)
|
||||||
let str = packet.packetString
|
let str = packet.packetString
|
||||||
|
|
||||||
DefaultSocketLogger.Logger.log("Emitting Ack: \(str)", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Emitting Ack: \(str)", type: logType)
|
||||||
|
|
||||||
engine?.send(str, withData: packet.binary)
|
manager?.engine?.send(str, withData: packet.binary)
|
||||||
}
|
|
||||||
|
|
||||||
/// Called when the engine closes.
|
|
||||||
///
|
|
||||||
/// - parameter reason: The reason that the engine closed.
|
|
||||||
open func engineDidClose(reason: String) {
|
|
||||||
handleQueue.async {
|
|
||||||
self._engineDidClose(reason: reason)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func _engineDidClose(reason: String) {
|
|
||||||
waitingPackets.removeAll()
|
|
||||||
|
|
||||||
if status != .disconnected {
|
|
||||||
status = .notConnected
|
|
||||||
}
|
|
||||||
|
|
||||||
if status == .disconnected || !reconnects {
|
|
||||||
didDisconnect(reason: reason)
|
|
||||||
} else if !reconnecting {
|
|
||||||
reconnecting = true
|
|
||||||
tryReconnect(reason: reason)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called when the engine errors.
|
|
||||||
///
|
|
||||||
/// - parameter reason: The reason the engine errored.
|
|
||||||
open func engineDidError(reason: String) {
|
|
||||||
handleQueue.async {
|
|
||||||
self._engineDidError(reason: reason)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func _engineDidError(reason: String) {
|
|
||||||
DefaultSocketLogger.Logger.error("\(reason)", type: SocketIOClient.logType)
|
|
||||||
|
|
||||||
handleClientEvent(.error, data: [reason])
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called when the engine opens.
|
|
||||||
///
|
|
||||||
/// - parameter reason: The reason the engine opened.
|
|
||||||
open func engineDidOpen(reason: String) {
|
|
||||||
handleQueue.async {
|
|
||||||
self._engineDidOpen(reason: reason)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func _engineDidOpen(reason: String) {
|
|
||||||
DefaultSocketLogger.Logger.log("Engine opened \(reason)", type: SocketIOClient.logType)
|
|
||||||
|
|
||||||
guard nsp != "/" else {
|
|
||||||
didConnect(toNamespace: "/")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
joinNamespace(nsp)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called when the engine receives a pong message.
|
|
||||||
open func engineDidReceivePong() {
|
|
||||||
handleQueue.async {
|
|
||||||
self._engineDidReceivePong()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func _engineDidReceivePong() {
|
|
||||||
handleClientEvent(.pong, data: [])
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called when the sends a ping to the server.
|
|
||||||
open func engineDidSendPing() {
|
|
||||||
handleQueue.async {
|
|
||||||
self._engineDidSendPing()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func _engineDidSendPing() {
|
|
||||||
handleClientEvent(.ping, data: [])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called when socket.io has acked one of our emits. Causes the corresponding ack callback to be called.
|
/// Called when socket.io has acked one of our emits. Causes the corresponding ack callback to be called.
|
||||||
@ -483,11 +308,19 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
/// - parameter data: The data sent back with this ack.
|
/// - parameter data: The data sent back with this ack.
|
||||||
@objc
|
@objc
|
||||||
open func handleAck(_ ack: Int, data: [Any]) {
|
open func handleAck(_ ack: Int, data: [Any]) {
|
||||||
guard status == .connected else { return }
|
guard status == .connected, let manager = self.manager else { return }
|
||||||
|
|
||||||
DefaultSocketLogger.Logger.log("Handling ack: \(ack) with data: \(data)", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Handling ack: \(ack) with data: \(data)", type: logType)
|
||||||
|
|
||||||
ackHandlers.executeAck(ack, with: data, onQueue: handleQueue)
|
ackHandlers.executeAck(ack, with: data, onQueue: manager.handleQueue)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called on socket.io specific events.
|
||||||
|
///
|
||||||
|
/// - parameter event: The `SocketClientEvent`.
|
||||||
|
/// - parameter data: The data for this event.
|
||||||
|
open func handleClientEvent(_ event: SocketClientEvent, data: [Any]) {
|
||||||
|
handleEvent(event.rawValue, data: data, isInternalMessage: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called when we get an event from socket.io.
|
/// Called when we get an event from socket.io.
|
||||||
@ -500,7 +333,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
open func handleEvent(_ event: String, data: [Any], isInternalMessage: Bool, withAck ack: Int = -1) {
|
open func handleEvent(_ event: String, data: [Any], isInternalMessage: Bool, withAck ack: Int = -1) {
|
||||||
guard status == .connected || isInternalMessage else { return }
|
guard status == .connected || isInternalMessage else { return }
|
||||||
|
|
||||||
DefaultSocketLogger.Logger.log("Handling event: \(event) with data: \(data)", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Handling event: \(event) with data: \(data)", type: logType)
|
||||||
|
|
||||||
anyHandler?(SocketAnyEvent(event: event, items: data))
|
anyHandler?(SocketAnyEvent(event: event, items: data))
|
||||||
|
|
||||||
@ -509,36 +342,49 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called on socket.io specific events.
|
/// Causes a client to handle a socket.io packet. The namespace for the packet must match the namespace of the
|
||||||
|
/// socket.
|
||||||
///
|
///
|
||||||
/// - parameter event: The `SocketClientEvent`.
|
/// - parameter pack: The packet to handle.
|
||||||
/// - parameter data: The data for this event.
|
open func handlePacket(_ pack: SocketPacket) {
|
||||||
open func handleClientEvent(_ event: SocketClientEvent, data: [Any]) {
|
func handleConnect(_ packetNamespace: String) {
|
||||||
handleEvent(event.rawValue, data: data, isInternalMessage: true)
|
guard packetNamespace == nsp else { return }
|
||||||
|
|
||||||
|
didConnect(toNamespace: packetNamespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call when you wish to leave a namespace and return to the default namespace.
|
switch pack.type {
|
||||||
|
case .event, .binaryEvent:
|
||||||
|
handleEvent(pack.event, data: pack.args, isInternalMessage: false, withAck: pack.id)
|
||||||
|
case .ack, .binaryAck:
|
||||||
|
handleAck(pack.id, data: pack.data)
|
||||||
|
case .connect:
|
||||||
|
handleConnect(pack.nsp)
|
||||||
|
case .disconnect:
|
||||||
|
didDisconnect(reason: "Got Disconnect")
|
||||||
|
case .error:
|
||||||
|
handleEvent("error", data: pack.data, isInternalMessage: true, withAck: pack.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call when you wish to leave a namespace and disconnect this socket.
|
||||||
@objc
|
@objc
|
||||||
open func leaveNamespace() {
|
open func leaveNamespace() {
|
||||||
guard nsp != "/" else { return }
|
guard nsp != "/" else { return }
|
||||||
|
|
||||||
engine?.send("1\(nsp)", withData: [])
|
status = .disconnected
|
||||||
nsp = "/"
|
|
||||||
|
manager?.disconnectSocket(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Joins `namespace`.
|
/// Joins `nsp`.
|
||||||
///
|
|
||||||
/// **Do not use this to join the default namespace.** Instead call `leaveNamespace`.
|
|
||||||
///
|
|
||||||
/// - parameter namespace: The namespace to join.
|
|
||||||
@objc
|
@objc
|
||||||
open func joinNamespace(_ namespace: String) {
|
open func joinNamespace() {
|
||||||
guard namespace != "/" else { return }
|
guard nsp != "/" else { return }
|
||||||
|
|
||||||
DefaultSocketLogger.Logger.log("Joining namespace \(namespace)", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Joining namespace \(nsp)", type: logType)
|
||||||
|
|
||||||
nsp = namespace
|
manager?.engine?.send("0\(nsp)", withData: [])
|
||||||
engine?.send("0\(nsp)", withData: [])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes handler(s) for a client event.
|
/// Removes handler(s) for a client event.
|
||||||
@ -557,7 +403,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
/// - parameter event: The event to remove handlers for.
|
/// - parameter event: The event to remove handlers for.
|
||||||
@objc
|
@objc
|
||||||
open func off(_ event: String) {
|
open func off(_ event: String) {
|
||||||
DefaultSocketLogger.Logger.log("Removing handler for event: \(event)", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Removing handler for event: \(event)", type: logType)
|
||||||
|
|
||||||
handlers = handlers.filter({ $0.event != event })
|
handlers = handlers.filter({ $0.event != event })
|
||||||
}
|
}
|
||||||
@ -569,7 +415,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
/// - parameter id: The UUID of the handler you wish to remove.
|
/// - parameter id: The UUID of the handler you wish to remove.
|
||||||
@objc
|
@objc
|
||||||
open func off(id: UUID) {
|
open func off(id: UUID) {
|
||||||
DefaultSocketLogger.Logger.log("Removing handler with id: \(id)", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Removing handler with id: \(id)", type: logType)
|
||||||
|
|
||||||
handlers = handlers.filter({ $0.id != id })
|
handlers = handlers.filter({ $0.id != id })
|
||||||
}
|
}
|
||||||
@ -582,7 +428,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
@objc
|
@objc
|
||||||
@discardableResult
|
@discardableResult
|
||||||
open func on(_ event: String, callback: @escaping NormalCallback) -> UUID {
|
open func on(_ event: String, callback: @escaping NormalCallback) -> UUID {
|
||||||
DefaultSocketLogger.Logger.log("Adding handler for event: \(event)", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Adding handler for event: \(event)", type: logType)
|
||||||
|
|
||||||
let handler = SocketEventHandler(event: event, id: UUID(), callback: callback)
|
let handler = SocketEventHandler(event: event, id: UUID(), callback: callback)
|
||||||
handlers.append(handler)
|
handlers.append(handler)
|
||||||
@ -626,7 +472,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
@objc
|
@objc
|
||||||
@discardableResult
|
@discardableResult
|
||||||
open func once(_ event: String, callback: @escaping NormalCallback) -> UUID {
|
open func once(_ event: String, callback: @escaping NormalCallback) -> UUID {
|
||||||
DefaultSocketLogger.Logger.log("Adding once handler for event: \(event)", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Adding once handler for event: \(event)", type: logType)
|
||||||
|
|
||||||
let id = UUID()
|
let id = UUID()
|
||||||
|
|
||||||
@ -649,31 +495,10 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
anyHandler = handler
|
anyHandler = handler
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called when the engine has a message that must be parsed.
|
|
||||||
///
|
|
||||||
/// - parameter msg: The message that needs parsing.
|
|
||||||
public func parseEngineMessage(_ msg: String) {
|
|
||||||
DefaultSocketLogger.Logger.log("Should parse message: \(msg)", type: SocketIOClient.logType)
|
|
||||||
|
|
||||||
handleQueue.async { self.parseSocketMessage(msg) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called when the engine receives binary data.
|
|
||||||
///
|
|
||||||
/// - parameter data: The data the engine received.
|
|
||||||
public func parseEngineBinaryData(_ data: Data) {
|
|
||||||
handleQueue.async { self.parseBinaryData(data) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tries to reconnect to the server.
|
/// Tries to reconnect to the server.
|
||||||
///
|
|
||||||
/// This will cause a `disconnect` event to be emitted, as well as an `reconnectAttempt` event.
|
|
||||||
@objc
|
@objc
|
||||||
open func reconnect() {
|
@available(*, unavailable, message: "Call the manager's reconnect method")
|
||||||
guard !reconnecting else { return }
|
open func reconnect() { }
|
||||||
|
|
||||||
engine?.disconnect(reason: "manual reconnect")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes all handlers.
|
/// Removes all handlers.
|
||||||
///
|
///
|
||||||
@ -683,54 +508,15 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
handlers.removeAll(keepingCapacity: false)
|
handlers.removeAll(keepingCapacity: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func tryReconnect(reason: String) {
|
/// Puts the socket back into the connecting state.
|
||||||
guard reconnecting else { return }
|
/// Called when the manager detects a broken connection, or when a manual reconnect is triggered.
|
||||||
|
///
|
||||||
|
/// - parameter reason: The reason this socket is reconnecting.
|
||||||
|
@objc
|
||||||
|
open func setReconnecting(reason: String) {
|
||||||
|
status = .connecting
|
||||||
|
|
||||||
DefaultSocketLogger.Logger.log("Starting reconnect", type: SocketIOClient.logType)
|
|
||||||
handleClientEvent(.reconnect, data: [reason])
|
handleClientEvent(.reconnect, data: [reason])
|
||||||
|
|
||||||
_tryReconnect()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func _tryReconnect() {
|
|
||||||
guard reconnects && reconnecting && status != .disconnected else { return }
|
|
||||||
|
|
||||||
if reconnectAttempts != -1 && currentReconnectAttempt + 1 > reconnectAttempts {
|
|
||||||
return didDisconnect(reason: "Reconnect Failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
DefaultSocketLogger.Logger.log("Trying to reconnect", type: SocketIOClient.logType)
|
|
||||||
handleClientEvent(.reconnectAttempt, data: [(reconnectAttempts - currentReconnectAttempt)])
|
|
||||||
|
|
||||||
currentReconnectAttempt += 1
|
|
||||||
connect()
|
|
||||||
|
|
||||||
handleQueue.asyncAfter(deadline: DispatchTime.now() + Double(reconnectWait), execute: _tryReconnect)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setConfigs() {
|
|
||||||
for option in config {
|
|
||||||
switch option {
|
|
||||||
case let .reconnects(reconnects):
|
|
||||||
self.reconnects = reconnects
|
|
||||||
case let .reconnectAttempts(attempts):
|
|
||||||
reconnectAttempts = attempts
|
|
||||||
case let .reconnectWait(wait):
|
|
||||||
reconnectWait = abs(wait)
|
|
||||||
case let .nsp(nsp):
|
|
||||||
self.nsp = nsp
|
|
||||||
case let .log(log):
|
|
||||||
DefaultSocketLogger.Logger.log = log
|
|
||||||
case let .logger(logger):
|
|
||||||
DefaultSocketLogger.Logger = logger
|
|
||||||
case let .handleQueue(queue):
|
|
||||||
handleQueue = queue
|
|
||||||
case let .forceNew(force):
|
|
||||||
forceNew = force
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test properties
|
// Test properties
|
||||||
@ -743,14 +529,10 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
status = .connected
|
status = .connected
|
||||||
}
|
}
|
||||||
|
|
||||||
func setTestStatus(_ status: SocketIOClientStatus) {
|
func setTestStatus(_ status: SocketIOStatus) {
|
||||||
self.status = status
|
self.status = status
|
||||||
}
|
}
|
||||||
|
|
||||||
func setTestEngine(_ engine: SocketEngineSpec?) {
|
|
||||||
self.engine = engine
|
|
||||||
}
|
|
||||||
|
|
||||||
func emitTest(event: String, _ data: Any...) {
|
func emitTest(event: String, _ data: Any...) {
|
||||||
emit([event] + data)
|
emit([event] + data)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -125,5 +125,12 @@ public struct SocketIOClientConfiguration : ExpressibleByArrayLiteral, Collectio
|
|||||||
|
|
||||||
backingArray.append(element)
|
backingArray.append(element)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Declares that a type can set configs from a `SocketIOClientConfiguration`.
|
||||||
|
public protocol ConfigSettable {
|
||||||
|
/// Called when an `ConfigSettable` should set/update its configs from a given configuration.
|
||||||
|
///
|
||||||
|
/// - parameter config: The `SocketIOClientConfiguration` that should be used to set/update configs.
|
||||||
|
mutating func setConfigs(_ config: SocketIOClientConfiguration)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,10 +40,6 @@ public enum SocketIOClientOption : ClientOption {
|
|||||||
/// An array of cookies that will be sent during the initial connection.
|
/// An array of cookies that will be sent during the initial connection.
|
||||||
case cookies([HTTPCookie])
|
case cookies([HTTPCookie])
|
||||||
|
|
||||||
/// Deprecated
|
|
||||||
@available(*, deprecated, message: "No longer needed in socket.io 2.0+")
|
|
||||||
case doubleEncodeUTF8(Bool)
|
|
||||||
|
|
||||||
/// Any extra HTTP headers that should be sent during the initial connection.
|
/// Any extra HTTP headers that should be sent during the initial connection.
|
||||||
case extraHeaders([String: String])
|
case extraHeaders([String: String])
|
||||||
|
|
||||||
@ -67,10 +63,6 @@ public enum SocketIOClientOption : ClientOption {
|
|||||||
/// Used to pass in a custom logger.
|
/// Used to pass in a custom logger.
|
||||||
case logger(SocketLogger)
|
case logger(SocketLogger)
|
||||||
|
|
||||||
/// The namespace that this client should connect to. Can be changed during use using the `joinNamespace`
|
|
||||||
/// and `leaveNamespace` methods on `SocketIOClient`.
|
|
||||||
case nsp(String)
|
|
||||||
|
|
||||||
/// A custom path to socket.io. Only use this if the socket.io server is configured to look for this path.
|
/// A custom path to socket.io. Only use this if the socket.io server is configured to look for this path.
|
||||||
case path(String)
|
case path(String)
|
||||||
|
|
||||||
@ -96,11 +88,6 @@ public enum SocketIOClientOption : ClientOption {
|
|||||||
/// Sets an NSURLSessionDelegate for the underlying engine. Useful if you need to handle self-signed certs.
|
/// Sets an NSURLSessionDelegate for the underlying engine. Useful if you need to handle self-signed certs.
|
||||||
case sessionDelegate(URLSessionDelegate)
|
case sessionDelegate(URLSessionDelegate)
|
||||||
|
|
||||||
/// If passed `true`, the WebSocket transport will try and use voip logic to keep network connections open in
|
|
||||||
/// the background. **This option is experimental as socket.io shouldn't be used for background communication.**
|
|
||||||
@available(*, deprecated, message: "No longer has any effect, and will be removed in v11.0")
|
|
||||||
case voipEnabled(Bool)
|
|
||||||
|
|
||||||
// MARK: Properties
|
// MARK: Properties
|
||||||
|
|
||||||
/// The description of this option.
|
/// The description of this option.
|
||||||
@ -114,8 +101,6 @@ public enum SocketIOClientOption : ClientOption {
|
|||||||
description = "connectParams"
|
description = "connectParams"
|
||||||
case .cookies:
|
case .cookies:
|
||||||
description = "cookies"
|
description = "cookies"
|
||||||
case .doubleEncodeUTF8:
|
|
||||||
description = "doubleEncodeUTF8"
|
|
||||||
case .extraHeaders:
|
case .extraHeaders:
|
||||||
description = "extraHeaders"
|
description = "extraHeaders"
|
||||||
case .forceNew:
|
case .forceNew:
|
||||||
@ -130,8 +115,6 @@ public enum SocketIOClientOption : ClientOption {
|
|||||||
description = "log"
|
description = "log"
|
||||||
case .logger:
|
case .logger:
|
||||||
description = "logger"
|
description = "logger"
|
||||||
case .nsp:
|
|
||||||
description = "nsp"
|
|
||||||
case .path:
|
case .path:
|
||||||
description = "path"
|
description = "path"
|
||||||
case .reconnects:
|
case .reconnects:
|
||||||
@ -148,8 +131,6 @@ public enum SocketIOClientOption : ClientOption {
|
|||||||
description = "security"
|
description = "security"
|
||||||
case .sessionDelegate:
|
case .sessionDelegate:
|
||||||
description = "sessionDelegate"
|
description = "sessionDelegate"
|
||||||
case .voipEnabled:
|
|
||||||
description = "voipEnabled"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return description
|
return description
|
||||||
@ -165,8 +146,6 @@ public enum SocketIOClientOption : ClientOption {
|
|||||||
value = params
|
value = params
|
||||||
case let .cookies(cookies):
|
case let .cookies(cookies):
|
||||||
value = cookies
|
value = cookies
|
||||||
case let .doubleEncodeUTF8(encode):
|
|
||||||
value = encode
|
|
||||||
case let .extraHeaders(headers):
|
case let .extraHeaders(headers):
|
||||||
value = headers
|
value = headers
|
||||||
case let .forceNew(force):
|
case let .forceNew(force):
|
||||||
@ -181,8 +160,6 @@ public enum SocketIOClientOption : ClientOption {
|
|||||||
value = log
|
value = log
|
||||||
case let .logger(logger):
|
case let .logger(logger):
|
||||||
value = logger
|
value = logger
|
||||||
case let .nsp(nsp):
|
|
||||||
value = nsp
|
|
||||||
case let .path(path):
|
case let .path(path):
|
||||||
value = path
|
value = path
|
||||||
case let .reconnects(reconnects):
|
case let .reconnects(reconnects):
|
||||||
@ -199,8 +176,6 @@ public enum SocketIOClientOption : ClientOption {
|
|||||||
value = signed
|
value = signed
|
||||||
case let .sessionDelegate(delegate):
|
case let .sessionDelegate(delegate):
|
||||||
value = delegate
|
value = delegate
|
||||||
case let .voipEnabled(enabled):
|
|
||||||
value = enabled
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|||||||
@ -32,22 +32,19 @@ public protocol SocketIOClientSpec : class {
|
|||||||
/// A handler that will be called on any event.
|
/// A handler that will be called on any event.
|
||||||
var anyHandler: ((SocketAnyEvent) -> ())? { get }
|
var anyHandler: ((SocketAnyEvent) -> ())? { get }
|
||||||
|
|
||||||
/// The configuration for this client.
|
|
||||||
var config: SocketIOClientConfiguration { get set }
|
|
||||||
|
|
||||||
/// The queue that all interaction with the client must be on.
|
|
||||||
var handleQueue: DispatchQueue { get set }
|
|
||||||
|
|
||||||
/// The array of handlers for this socket.
|
/// The array of handlers for this socket.
|
||||||
var handlers: [SocketEventHandler] { get }
|
var handlers: [SocketEventHandler] { get }
|
||||||
|
|
||||||
|
/// The manager for this socket.
|
||||||
|
var manager: SocketManagerSpec? { get }
|
||||||
|
|
||||||
/// The namespace that this socket is currently connected to.
|
/// The namespace that this socket is currently connected to.
|
||||||
///
|
///
|
||||||
/// **Must** start with a `/`.
|
/// **Must** start with a `/`.
|
||||||
var nsp: String { get set }
|
var nsp: String { get set }
|
||||||
|
|
||||||
/// The status of this client.
|
/// The status of this client.
|
||||||
var status: SocketIOClientStatus { get }
|
var status: SocketIOStatus { get }
|
||||||
|
|
||||||
// MARK: Methods
|
// MARK: Methods
|
||||||
|
|
||||||
@ -126,6 +123,12 @@ public protocol SocketIOClientSpec : class {
|
|||||||
/// - parameter data: The data sent back with this ack.
|
/// - parameter data: The data sent back with this ack.
|
||||||
func handleAck(_ ack: Int, data: [Any])
|
func handleAck(_ ack: Int, data: [Any])
|
||||||
|
|
||||||
|
/// Called on socket.io specific events.
|
||||||
|
///
|
||||||
|
/// - parameter event: The `SocketClientEvent`.
|
||||||
|
/// - parameter data: The data for this event.
|
||||||
|
func handleClientEvent(_ event: SocketClientEvent, data: [Any])
|
||||||
|
|
||||||
/// Called when we get an event from socket.io.
|
/// Called when we get an event from socket.io.
|
||||||
///
|
///
|
||||||
/// - parameter event: The name of the event.
|
/// - parameter event: The name of the event.
|
||||||
@ -134,21 +137,17 @@ public protocol SocketIOClientSpec : class {
|
|||||||
/// - parameter withAck: If > 0 then this event expects to get an ack back from the client.
|
/// - parameter withAck: If > 0 then this event expects to get an ack back from the client.
|
||||||
func handleEvent(_ event: String, data: [Any], isInternalMessage: Bool, withAck ack: Int)
|
func handleEvent(_ event: String, data: [Any], isInternalMessage: Bool, withAck ack: Int)
|
||||||
|
|
||||||
/// Called on socket.io specific events.
|
/// Causes a client to handle a socket.io packet. The namespace for the packet must match the namespace of the
|
||||||
|
/// socket.
|
||||||
///
|
///
|
||||||
/// - parameter event: The `SocketClientEvent`.
|
/// - parameter pack: The packet to handle.
|
||||||
/// - parameter data: The data for this event.
|
func handlePacket(_ pack: SocketPacket)
|
||||||
func handleClientEvent(_ event: SocketClientEvent, data: [Any])
|
|
||||||
|
|
||||||
/// Call when you wish to leave a namespace and return to the default namespace.
|
/// Call when you wish to leave a namespace and disconnect this socket.
|
||||||
func leaveNamespace()
|
func leaveNamespace()
|
||||||
|
|
||||||
/// Joins `namespace`.
|
/// Joins `nsp`.
|
||||||
///
|
func joinNamespace()
|
||||||
/// **Do not use this to join the default namespace.** Instead call `leaveNamespace`.
|
|
||||||
///
|
|
||||||
/// - parameter namespace: The namespace to join.
|
|
||||||
func joinNamespace(_ namespace: String)
|
|
||||||
|
|
||||||
/// Removes handler(s) for a client event.
|
/// Removes handler(s) for a client event.
|
||||||
///
|
///
|
||||||
@ -212,13 +211,16 @@ public protocol SocketIOClientSpec : class {
|
|||||||
/// - parameter handler: The callback that will execute whenever an event is received.
|
/// - parameter handler: The callback that will execute whenever an event is received.
|
||||||
func onAny(_ handler: @escaping (SocketAnyEvent) -> ())
|
func onAny(_ handler: @escaping (SocketAnyEvent) -> ())
|
||||||
|
|
||||||
/// Tries to reconnect to the server.
|
|
||||||
func reconnect()
|
|
||||||
|
|
||||||
/// Removes all handlers.
|
/// Removes all handlers.
|
||||||
///
|
///
|
||||||
/// Can be used after disconnecting to break any potential remaining retain cycles.
|
/// Can be used after disconnecting to break any potential remaining retain cycles.
|
||||||
func removeAllHandlers()
|
func removeAllHandlers()
|
||||||
|
|
||||||
|
/// Puts the socket back into the connecting state.
|
||||||
|
/// Called when the manager detects a broken connection, or when a manual reconnect is triggered.
|
||||||
|
///
|
||||||
|
/// parameter reason: The reason this socket is going reconnecting.
|
||||||
|
func setReconnecting(reason: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension SocketIOClientSpec {
|
public extension SocketIOClientSpec {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// SocketIOClientStatus.swift
|
// SocketIOStatus.swift
|
||||||
// Socket.IO-Client-Swift
|
// Socket.IO-Client-Swift
|
||||||
//
|
//
|
||||||
// Created by Erik Little on 8/14/15.
|
// Created by Erik Little on 8/14/15.
|
||||||
@ -24,17 +24,36 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/// Represents the state of the client.
|
/// Represents state of a manager or client.
|
||||||
@objc public enum SocketIOClientStatus : Int {
|
@objc
|
||||||
/// The client has never been connected. Or the client has been reset.
|
public enum SocketIOStatus : Int, CustomStringConvertible {
|
||||||
|
// MARK: Cases
|
||||||
|
|
||||||
|
/// The client/manager has never been connected. Or the client has been reset.
|
||||||
case notConnected
|
case notConnected
|
||||||
|
|
||||||
/// The client was once connected, but not anymore.
|
/// The client/manager was once connected, but not anymore.
|
||||||
case disconnected
|
case disconnected
|
||||||
|
|
||||||
/// The client is in the process of connecting.
|
/// The client/manager is in the process of connecting.
|
||||||
case connecting
|
case connecting
|
||||||
|
|
||||||
/// The client is currently connected.
|
/// The client/manager is currently connected.
|
||||||
case connected
|
case connected
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
/// - returns: True if this client/manager is connected/connecting to a server.
|
||||||
|
public var active: Bool {
|
||||||
|
return self == .connected || self == .connecting
|
||||||
|
}
|
||||||
|
|
||||||
|
public var description: String {
|
||||||
|
switch self {
|
||||||
|
case .connected: return "connected"
|
||||||
|
case .connecting: return "connecting"
|
||||||
|
case .disconnected: return "disconnected"
|
||||||
|
case .notConnected: return "notConnected"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -28,7 +28,8 @@ import Starscream
|
|||||||
|
|
||||||
/// The class that handles the engine.io protocol and transports.
|
/// The class that handles the engine.io protocol and transports.
|
||||||
/// See `SocketEnginePollable` and `SocketEngineWebsocket` for transport specific methods.
|
/// See `SocketEnginePollable` and `SocketEngineWebsocket` for transport specific methods.
|
||||||
public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, SocketEngineWebsocket {
|
public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, SocketEngineWebsocket,
|
||||||
|
ConfigSettable {
|
||||||
// MARK: Properties
|
// MARK: Properties
|
||||||
|
|
||||||
private static let logType = "SocketEngine"
|
private static let logType = "SocketEngine"
|
||||||
@ -147,41 +148,11 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
|
|||||||
public init(client: SocketEngineClient, url: URL, config: SocketIOClientConfiguration) {
|
public init(client: SocketEngineClient, url: URL, config: SocketIOClientConfiguration) {
|
||||||
self.client = client
|
self.client = client
|
||||||
self.url = url
|
self.url = url
|
||||||
for option in config {
|
|
||||||
switch option {
|
|
||||||
case let .connectParams(params):
|
|
||||||
connectParams = params
|
|
||||||
case let .cookies(cookies):
|
|
||||||
self.cookies = cookies
|
|
||||||
case let .extraHeaders(headers):
|
|
||||||
extraHeaders = headers
|
|
||||||
case let .sessionDelegate(delegate):
|
|
||||||
sessionDelegate = delegate
|
|
||||||
case let .forcePolling(force):
|
|
||||||
forcePolling = force
|
|
||||||
case let .forceWebsockets(force):
|
|
||||||
forceWebsockets = force
|
|
||||||
case let .path(path):
|
|
||||||
socketPath = path
|
|
||||||
|
|
||||||
if !socketPath.hasSuffix("/") {
|
|
||||||
socketPath += "/"
|
|
||||||
}
|
|
||||||
case let .secure(secure):
|
|
||||||
self.secure = secure
|
|
||||||
case let .selfSigned(selfSigned):
|
|
||||||
self.selfSigned = selfSigned
|
|
||||||
case let .security(security):
|
|
||||||
self.security = security
|
|
||||||
case .compress:
|
|
||||||
self.compress = true
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
setConfigs(config)
|
||||||
|
|
||||||
sessionDelegate = sessionDelegate ?? self
|
sessionDelegate = sessionDelegate ?? self
|
||||||
|
|
||||||
(urlPolling, urlWebSocket) = createURLs()
|
(urlPolling, urlWebSocket) = createURLs()
|
||||||
@ -572,6 +543,44 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
|
|||||||
client?.engineDidSendPing()
|
client?.engineDidSendPing()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called when the engine should set/update its configs from a given configuration.
|
||||||
|
///
|
||||||
|
/// parameter config: The `SocketIOClientConfiguration` that should be used to set/update configs.
|
||||||
|
open func setConfigs(_ config: SocketIOClientConfiguration) {
|
||||||
|
for option in config {
|
||||||
|
switch option {
|
||||||
|
case let .connectParams(params):
|
||||||
|
connectParams = params
|
||||||
|
case let .cookies(cookies):
|
||||||
|
self.cookies = cookies
|
||||||
|
case let .extraHeaders(headers):
|
||||||
|
extraHeaders = headers
|
||||||
|
case let .sessionDelegate(delegate):
|
||||||
|
sessionDelegate = delegate
|
||||||
|
case let .forcePolling(force):
|
||||||
|
forcePolling = force
|
||||||
|
case let .forceWebsockets(force):
|
||||||
|
forceWebsockets = force
|
||||||
|
case let .path(path):
|
||||||
|
socketPath = path
|
||||||
|
|
||||||
|
if !socketPath.hasSuffix("/") {
|
||||||
|
socketPath += "/"
|
||||||
|
}
|
||||||
|
case let .secure(secure):
|
||||||
|
self.secure = secure
|
||||||
|
case let .selfSigned(selfSigned):
|
||||||
|
self.selfSigned = selfSigned
|
||||||
|
case let .security(security):
|
||||||
|
self.security = security
|
||||||
|
case .compress:
|
||||||
|
self.compress = true
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Moves from long-polling to websockets
|
// Moves from long-polling to websockets
|
||||||
private func upgradeTransport() {
|
private func upgradeTransport() {
|
||||||
if ws?.isConnected ?? false {
|
if ws?.isConnected ?? false {
|
||||||
@ -645,6 +654,12 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
|
|||||||
client?.engineDidClose(reason: "Socket Disconnected")
|
client?.engineDidClose(reason: "Socket Disconnected")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test Properties
|
||||||
|
|
||||||
|
func setConnected(_ value: Bool) {
|
||||||
|
connected = value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SocketEngine {
|
extension SocketEngine {
|
||||||
|
|||||||
@ -34,6 +34,9 @@ import Starscream
|
|||||||
/// `true` if this engine is closed.
|
/// `true` if this engine is closed.
|
||||||
var closed: Bool { get }
|
var closed: Bool { get }
|
||||||
|
|
||||||
|
/// If `true` the engine will attempt to use WebSocket compression.
|
||||||
|
var compress: Bool { get }
|
||||||
|
|
||||||
/// `true` if this engine is connected. Connected means that the initial poll connect has succeeded.
|
/// `true` if this engine is connected. Connected means that the initial poll connect has succeeded.
|
||||||
var connected: Bool { get }
|
var connected: Bool { get }
|
||||||
|
|
||||||
|
|||||||
519
Source/SocketIO/Manager/SocketManager.swift
Normal file
519
Source/SocketIO/Manager/SocketManager.swift
Normal file
@ -0,0 +1,519 @@
|
|||||||
|
//
|
||||||
|
// Created by Erik Little on 10/14/17.
|
||||||
|
//
|
||||||
|
// 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 Dispatch
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
///
|
||||||
|
/// A manager for a socket.io connection.
|
||||||
|
///
|
||||||
|
/// A `SocketManager` is responsible for multiplexing multiple namespaces through a single `SocketEngineSpec`.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// ```swift
|
||||||
|
/// let manager = SocketManager(socketURL: URL(string:"http://localhost:8080/")!)
|
||||||
|
/// let defaultNamespaceSocket = manager.defaultSocket
|
||||||
|
/// let swiftSocket = manager.socket(forNamespace: "/swift")
|
||||||
|
///
|
||||||
|
/// // defaultNamespaceSocket and swiftSocket both share a single connection to the server
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Sockets created through the manager are retained by the manager. So at the very least, a single strong reference
|
||||||
|
/// to the manager must be maintained to keep sockets alive.
|
||||||
|
///
|
||||||
|
/// To disconnect a socket and remove it from the manager, either call `SocketIOClient.disconnect()` on the socket,
|
||||||
|
/// or call one of the `disconnectSocket` methods on this class.
|
||||||
|
///
|
||||||
|
open class SocketManager : NSObject, SocketManagerSpec, SocketParsable, SocketDataBufferable, ConfigSettable {
|
||||||
|
private static let logType = "SocketManager"
|
||||||
|
|
||||||
|
// MARK Properties
|
||||||
|
|
||||||
|
/// The socket associated with the default namespace ("/").
|
||||||
|
public var defaultSocket: SocketIOClient? {
|
||||||
|
return socket(forNamespace: "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The URL of the socket.io server.
|
||||||
|
///
|
||||||
|
/// If changed after calling `init`, `forceNew` must be set to `true`, or it will only connect to the url set in the
|
||||||
|
/// init.
|
||||||
|
public let socketURL: URL
|
||||||
|
|
||||||
|
/// The configuration for this client.
|
||||||
|
///
|
||||||
|
/// **Some configs will not take affect until after a reconnect if set after calling a connect method**.
|
||||||
|
public var config: SocketIOClientConfiguration {
|
||||||
|
get {
|
||||||
|
return _config
|
||||||
|
}
|
||||||
|
|
||||||
|
set {
|
||||||
|
if status.active {
|
||||||
|
DefaultSocketLogger.Logger.log("Setting configs on active manager. Some configs may not be applied until reconnect",
|
||||||
|
type: SocketManager.logType)
|
||||||
|
}
|
||||||
|
|
||||||
|
setConfigs(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The engine for this manager.
|
||||||
|
public var engine: SocketEngineSpec?
|
||||||
|
|
||||||
|
/// If `true` then every time `connect` is called, a new engine will be created.
|
||||||
|
public var forceNew = false
|
||||||
|
|
||||||
|
/// The queue that all interaction with the client should occur on. This is the queue that event handlers are
|
||||||
|
/// called on.
|
||||||
|
///
|
||||||
|
/// **This should be a serial queue! Concurrent queues are not supported and might cause crashes and races**.
|
||||||
|
public var handleQueue = DispatchQueue.main
|
||||||
|
|
||||||
|
/// The sockets in this manager indexed by namespace.
|
||||||
|
public var nsps = [String: SocketIOClient]()
|
||||||
|
|
||||||
|
/// If `true`, this client will try and reconnect on any disconnects.
|
||||||
|
public var reconnects = true
|
||||||
|
|
||||||
|
/// The number of seconds to wait before attempting to reconnect.
|
||||||
|
public var reconnectWait = 10
|
||||||
|
|
||||||
|
/// The status of this manager.
|
||||||
|
public private(set) var status: SocketIOStatus = .notConnected {
|
||||||
|
didSet {
|
||||||
|
switch status {
|
||||||
|
case .connected:
|
||||||
|
reconnecting = false
|
||||||
|
currentReconnectAttempt = 0
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A list of packets that are waiting for binary data.
|
||||||
|
///
|
||||||
|
/// The way that socket.io works all data should be sent directly after each packet.
|
||||||
|
/// So this should ideally be an array of one packet waiting for data.
|
||||||
|
///
|
||||||
|
/// **This should not be modified directly.**
|
||||||
|
public var waitingPackets = [SocketPacket]()
|
||||||
|
|
||||||
|
private(set) var reconnectAttempts = -1
|
||||||
|
|
||||||
|
private var _config: SocketIOClientConfiguration
|
||||||
|
private var currentReconnectAttempt = 0
|
||||||
|
private var reconnecting = false
|
||||||
|
|
||||||
|
/// Type safe way to create a new SocketIOClient. `opts` can be omitted.
|
||||||
|
///
|
||||||
|
/// - parameter socketURL: The url of the socket.io server.
|
||||||
|
/// - parameter config: The config for this socket.
|
||||||
|
public init(socketURL: URL, config: SocketIOClientConfiguration = []) {
|
||||||
|
self._config = config
|
||||||
|
self.socketURL = socketURL
|
||||||
|
|
||||||
|
if socketURL.absoluteString.hasPrefix("https://") {
|
||||||
|
self._config.insert(.secure(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
self._config.insert(.path("/socket.io/"), replacing: false)
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
setConfigs(_config)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Not so type safe way to create a SocketIOClient, meant for Objective-C compatiblity.
|
||||||
|
/// If using Swift it's recommended to use `init(socketURL: NSURL, options: Set<SocketIOClientOption>)`
|
||||||
|
///
|
||||||
|
/// - parameter socketURL: The url of the socket.io server.
|
||||||
|
/// - parameter config: The config for this socket.
|
||||||
|
@objc
|
||||||
|
public convenience init(socketURL: NSURL, config: NSDictionary?) {
|
||||||
|
self.init(socketURL: socketURL as URL, config: config?.toSocketConfiguration() ?? [])
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
DefaultSocketLogger.Logger.log("Manager is being released", type: SocketManager.logType)
|
||||||
|
|
||||||
|
engine?.disconnect(reason: "Manager Deinit")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Methods
|
||||||
|
|
||||||
|
private func addEngine() {
|
||||||
|
DefaultSocketLogger.Logger.log("Adding engine", type: SocketManager.logType)
|
||||||
|
|
||||||
|
engine?.engineQueue.sync {
|
||||||
|
self.engine?.client = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
engine = SocketEngine(client: self, url: socketURL, config: config)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connects the underlying transport and the default namespace socket.
|
||||||
|
///
|
||||||
|
/// Override if you wish to attach a custom `SocketEngineSpec`.
|
||||||
|
open func connect() {
|
||||||
|
guard !status.active else {
|
||||||
|
DefaultSocketLogger.Logger.log("Tried connecting an already active socket", type: SocketManager.logType)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if engine == nil || forceNew {
|
||||||
|
addEngine()
|
||||||
|
}
|
||||||
|
|
||||||
|
status = .connecting
|
||||||
|
|
||||||
|
engine?.connect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connects a socket through this manager's engine.
|
||||||
|
///
|
||||||
|
/// - parameter socket: The socket who we should connect through this manager.
|
||||||
|
open func connectSocket(_ socket: SocketIOClient) {
|
||||||
|
guard status == .connected else {
|
||||||
|
DefaultSocketLogger.Logger.log("Tried connecting socket when engine isn't open. Connecting",
|
||||||
|
type: SocketManager.logType)
|
||||||
|
|
||||||
|
connect()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
engine?.send("0\(socket.nsp)", withData: [])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the manager has disconnected from socket.io.
|
||||||
|
///
|
||||||
|
/// - parameter reason: The reason for the disconnection.
|
||||||
|
open func didDisconnect(reason: String) {
|
||||||
|
forAll {socket in
|
||||||
|
socket.didDisconnect(reason: reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disconnects the manager and all associated sockets.
|
||||||
|
open func disconnect() {
|
||||||
|
DefaultSocketLogger.Logger.log("Manager closing", type: SocketManager.logType)
|
||||||
|
|
||||||
|
status = .disconnected
|
||||||
|
|
||||||
|
engine?.disconnect(reason: "Disconnect")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disconnects the given socket.
|
||||||
|
///
|
||||||
|
/// This will remove the socket for the manager's control, and make the socket instance useless and ready for
|
||||||
|
/// releasing.
|
||||||
|
///
|
||||||
|
/// - parameter socket: The socket to disconnect.
|
||||||
|
open func disconnectSocket(_ socket: SocketIOClient) {
|
||||||
|
// Make sure we remove socket from nsps
|
||||||
|
nsps.removeValue(forKey: socket.nsp)
|
||||||
|
|
||||||
|
engine?.send("1\(socket.nsp)", withData: [])
|
||||||
|
socket.didDisconnect(reason: "Namespace leave")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disconnects the socket associated with `forNamespace`.
|
||||||
|
///
|
||||||
|
/// This will remove the socket for the manager's control, and make the socket instance useless and ready for
|
||||||
|
/// releasing.
|
||||||
|
///
|
||||||
|
/// - parameter forNamespace: The namespace to disconnect from.
|
||||||
|
open func disconnectSocket(forNamespace nsp: String) {
|
||||||
|
guard let socket = nsps.removeValue(forKey: nsp) else {
|
||||||
|
DefaultSocketLogger.Logger.log("Could not find socket for \(nsp) to disconnect",
|
||||||
|
type: SocketManager.logType)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectSocket(socket)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends a client event to all sockets in `nsps`
|
||||||
|
///
|
||||||
|
/// - parameter clientEvent: The event to emit.
|
||||||
|
open func emitAll(clientEvent event: SocketClientEvent, data: [Any]) {
|
||||||
|
forAll {socket in
|
||||||
|
socket.handleClientEvent(event, data: data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends an event to the server on all namespaces in this manager.
|
||||||
|
///
|
||||||
|
/// - parameter event: The event to send.
|
||||||
|
/// - parameter items: The data to send with this event.
|
||||||
|
open func emitAll(_ event: String, _ items: SocketData...) {
|
||||||
|
guard let emitData = try? items.map({ try $0.socketRepresentation() }) else {
|
||||||
|
DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)",
|
||||||
|
type: SocketManager.logType)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
emitAll(event, withItems: emitData)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends an event to the server on all namespaces in this manager.
|
||||||
|
///
|
||||||
|
/// Same as `emitAll(_:_:)`, but meant for Objective-C.
|
||||||
|
///
|
||||||
|
/// - parameter event: The event to send.
|
||||||
|
/// - parameter withItems: The data to send with this event.
|
||||||
|
open func emitAll(_ event: String, withItems items: [Any]) {
|
||||||
|
forAll {socket in
|
||||||
|
socket.emit(event, with: items)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the engine closes.
|
||||||
|
///
|
||||||
|
/// - parameter reason: The reason that the engine closed.
|
||||||
|
open func engineDidClose(reason: String) {
|
||||||
|
handleQueue.async {
|
||||||
|
self._engineDidClose(reason: reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func _engineDidClose(reason: String) {
|
||||||
|
waitingPackets.removeAll()
|
||||||
|
|
||||||
|
if status != .disconnected {
|
||||||
|
status = .notConnected
|
||||||
|
}
|
||||||
|
|
||||||
|
if status == .disconnected || !reconnects {
|
||||||
|
didDisconnect(reason: reason)
|
||||||
|
} else if !reconnecting {
|
||||||
|
reconnecting = true
|
||||||
|
tryReconnect(reason: reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the engine errors.
|
||||||
|
///
|
||||||
|
/// - parameter reason: The reason the engine errored.
|
||||||
|
open func engineDidError(reason: String) {
|
||||||
|
handleQueue.async {
|
||||||
|
self._engineDidError(reason: reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func _engineDidError(reason: String) {
|
||||||
|
DefaultSocketLogger.Logger.error("\(reason)", type: SocketManager.logType)
|
||||||
|
|
||||||
|
emitAll(clientEvent: .error, data: [reason])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the engine opens.
|
||||||
|
///
|
||||||
|
/// - parameter reason: The reason the engine opened.
|
||||||
|
open func engineDidOpen(reason: String) {
|
||||||
|
handleQueue.async {
|
||||||
|
self._engineDidOpen(reason: reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func _engineDidOpen(reason: String) {
|
||||||
|
DefaultSocketLogger.Logger.log("Engine opened \(reason)", type: SocketManager.logType)
|
||||||
|
|
||||||
|
status = .connected
|
||||||
|
nsps["/"]?.didConnect(toNamespace: "/")
|
||||||
|
|
||||||
|
for (nsp, socket) in nsps where nsp != "/" && socket.status == .connecting {
|
||||||
|
connectSocket(socket)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the engine receives a pong message.
|
||||||
|
open func engineDidReceivePong() {
|
||||||
|
handleQueue.async {
|
||||||
|
self._engineDidReceivePong()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func _engineDidReceivePong() {
|
||||||
|
emitAll(clientEvent: .pong, data: [])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the sends a ping to the server.
|
||||||
|
open func engineDidSendPing() {
|
||||||
|
handleQueue.async {
|
||||||
|
self._engineDidSendPing()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func _engineDidSendPing() {
|
||||||
|
emitAll(clientEvent: .ping, data: [])
|
||||||
|
}
|
||||||
|
|
||||||
|
private func forAll(do: (SocketIOClient) throws -> ()) rethrows {
|
||||||
|
for (_, socket) in nsps {
|
||||||
|
try `do`(socket)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the engine has a message that must be parsed.
|
||||||
|
///
|
||||||
|
/// - parameter msg: The message that needs parsing.
|
||||||
|
open func parseEngineMessage(_ msg: String) {
|
||||||
|
handleQueue.async {
|
||||||
|
self._parseEngineMessage(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func _parseEngineMessage(_ msg: String) {
|
||||||
|
guard let packet = parseSocketMessage(msg) else { return }
|
||||||
|
guard packet.type != .binaryAck && packet.type != .binaryEvent else {
|
||||||
|
waitingPackets.append(packet)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
nsps[packet.nsp]?.handlePacket(packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the engine receives binary data.
|
||||||
|
///
|
||||||
|
/// - parameter data: The data the engine received.
|
||||||
|
open func parseEngineBinaryData(_ data: Data) {
|
||||||
|
handleQueue.async {
|
||||||
|
self._parseEngineBinaryData(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func _parseEngineBinaryData(_ data: Data) {
|
||||||
|
guard let packet = parseBinaryData(data) else { return }
|
||||||
|
|
||||||
|
nsps[packet.nsp]?.handlePacket(packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to reconnect to the server.
|
||||||
|
///
|
||||||
|
/// This will cause a `disconnect` event to be emitted, as well as an `reconnectAttempt` event.
|
||||||
|
open func reconnect() {
|
||||||
|
guard !reconnecting else { return }
|
||||||
|
|
||||||
|
engine?.disconnect(reason: "manual reconnect")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func tryReconnect(reason: String) {
|
||||||
|
guard reconnecting else { return }
|
||||||
|
|
||||||
|
DefaultSocketLogger.Logger.log("Starting reconnect", type: SocketManager.logType)
|
||||||
|
|
||||||
|
// Set status to connecting and emit reconnect for all sockets
|
||||||
|
forAll {socket in
|
||||||
|
guard socket.status == .connected else { return }
|
||||||
|
|
||||||
|
socket.setReconnecting(reason: reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
_tryReconnect()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func _tryReconnect() {
|
||||||
|
guard reconnects && reconnecting && status != .disconnected else { return }
|
||||||
|
|
||||||
|
if reconnectAttempts != -1 && currentReconnectAttempt + 1 > reconnectAttempts {
|
||||||
|
return didDisconnect(reason: "Reconnect Failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultSocketLogger.Logger.log("Trying to reconnect", type: SocketManager.logType)
|
||||||
|
emitAll(clientEvent: .reconnectAttempt, data: [(reconnectAttempts - currentReconnectAttempt)])
|
||||||
|
|
||||||
|
currentReconnectAttempt += 1
|
||||||
|
connect()
|
||||||
|
|
||||||
|
handleQueue.asyncAfter(deadline: DispatchTime.now() + Double(reconnectWait), execute: _tryReconnect)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets manager specific configs.
|
||||||
|
///
|
||||||
|
/// parameter config: The configs that should be set.
|
||||||
|
open func setConfigs(_ config: SocketIOClientConfiguration) {
|
||||||
|
for option in config {
|
||||||
|
switch option {
|
||||||
|
case let .forceNew(new):
|
||||||
|
self.forceNew = new
|
||||||
|
case let .reconnects(reconnects):
|
||||||
|
self.reconnects = reconnects
|
||||||
|
case let .reconnectWait(wait):
|
||||||
|
reconnectWait = abs(wait)
|
||||||
|
case let .log(log):
|
||||||
|
DefaultSocketLogger.Logger.log = log
|
||||||
|
case let .logger(logger):
|
||||||
|
DefaultSocketLogger.Logger = logger
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_config = config
|
||||||
|
_config.insert(.path("/socket.io/"), replacing: false)
|
||||||
|
|
||||||
|
// If `ConfigSettable` & `SocketEngineSpec`, update its configs.
|
||||||
|
if var settableEngine = engine as? ConfigSettable & SocketEngineSpec {
|
||||||
|
settableEngine.engineQueue.sync {
|
||||||
|
settableEngine.setConfigs(self._config)
|
||||||
|
}
|
||||||
|
|
||||||
|
engine = settableEngine
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a `SocketIOClient` for the given namespace. This socket shares a transport with the manager.
|
||||||
|
///
|
||||||
|
/// Calling multiple times returns the same socket.
|
||||||
|
///
|
||||||
|
/// Sockets created from this method are retained by the manager.
|
||||||
|
/// Call one of the `disconnectSocket` methods on this class to remove the socket from manager control.
|
||||||
|
/// Or call `SocketIOClient.disconnect()` on the client.
|
||||||
|
///
|
||||||
|
/// - parameter forNamespace: The namespace for the socket.
|
||||||
|
/// - returns: A `SocketIOClient` for the given namespace.
|
||||||
|
open func socket(forNamespace nsp: String) -> SocketIOClient {
|
||||||
|
assert(nsp.hasPrefix("/"), "forNamespace must have a leading /")
|
||||||
|
|
||||||
|
if let socket = nsps[nsp] {
|
||||||
|
return socket
|
||||||
|
}
|
||||||
|
|
||||||
|
let client = SocketIOClient(manager: self, nsp: nsp)
|
||||||
|
|
||||||
|
nsps[nsp] = client
|
||||||
|
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test properties
|
||||||
|
|
||||||
|
func setTestStatus(_ status: SocketIOStatus) {
|
||||||
|
self.status = status
|
||||||
|
}
|
||||||
|
}
|
||||||
128
Source/SocketIO/Manager/SocketManagerSpec.swift
Normal file
128
Source/SocketIO/Manager/SocketManagerSpec.swift
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
//
|
||||||
|
// Created by Erik Little on 10/18/17.
|
||||||
|
//
|
||||||
|
// 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 Dispatch
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// TODO Fix the types so that we aren't using concrete types
|
||||||
|
|
||||||
|
///
|
||||||
|
/// A manager for a socket.io connection.
|
||||||
|
///
|
||||||
|
/// A `SocketManagerSpec` is responsible for multiplexing multiple namespaces through a single `SocketEngineSpec`.
|
||||||
|
///
|
||||||
|
/// Example with `SocketManager`:
|
||||||
|
///
|
||||||
|
/// ```swift
|
||||||
|
/// let manager = SocketManager(socketURL: URL(string:"http://localhost:8080/")!)
|
||||||
|
/// let defaultNamespaceSocket = manager.defaultSocket
|
||||||
|
/// let swiftSocket = manager.socket(forNamespace: "/swift")
|
||||||
|
///
|
||||||
|
/// // defaultNamespaceSocket and swiftSocket both share a single connection to the server
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Sockets created through the manager are retained by the manager. So at the very least, a single strong reference
|
||||||
|
/// to the manager must be maintained to keep sockets alive.
|
||||||
|
///
|
||||||
|
/// To disconnect a socket and remove it from the manager, either call `SocketIOClient.disconnect()` on the socket,
|
||||||
|
/// or call one of the `disconnectSocket` methods on this class.
|
||||||
|
///
|
||||||
|
@objc
|
||||||
|
public protocol SocketManagerSpec : class, SocketEngineClient {
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
/// Returns the socket associated with the default namespace ("/").
|
||||||
|
var defaultSocket: SocketIOClient? { get }
|
||||||
|
|
||||||
|
/// The engine for this manager.
|
||||||
|
var engine: SocketEngineSpec? { get set }
|
||||||
|
|
||||||
|
/// If `true` then every time `connect` is called, a new engine will be created.
|
||||||
|
var forceNew: Bool { get set }
|
||||||
|
|
||||||
|
// TODO Per socket queues?
|
||||||
|
/// The queue that all interaction with the client should occur on. This is the queue that event handlers are
|
||||||
|
/// called on.
|
||||||
|
var handleQueue: DispatchQueue { get set }
|
||||||
|
|
||||||
|
/// If `true`, this manager will try and reconnect on any disconnects.
|
||||||
|
var reconnects: Bool { get set }
|
||||||
|
|
||||||
|
/// The number of seconds to wait before attempting to reconnect.
|
||||||
|
var reconnectWait: Int { get set }
|
||||||
|
|
||||||
|
/// The URL of the socket.io server.
|
||||||
|
var socketURL: URL { get }
|
||||||
|
|
||||||
|
/// The status of this manager.
|
||||||
|
var status: SocketIOStatus { get }
|
||||||
|
|
||||||
|
// MARK: Methods
|
||||||
|
|
||||||
|
/// Connects the underlying transport.
|
||||||
|
func connect()
|
||||||
|
|
||||||
|
/// Connects a socket through this manager's engine.
|
||||||
|
///
|
||||||
|
/// - parameter socket: The socket who we should connect through this manager.
|
||||||
|
func connectSocket(_ socket: SocketIOClient)
|
||||||
|
|
||||||
|
/// Called when the manager has disconnected from socket.io.
|
||||||
|
///
|
||||||
|
/// - parameter reason: The reason for the disconnection.
|
||||||
|
func didDisconnect(reason: String)
|
||||||
|
|
||||||
|
/// Disconnects the manager and all associated sockets.
|
||||||
|
func disconnect()
|
||||||
|
|
||||||
|
/// Disconnects the given socket.
|
||||||
|
///
|
||||||
|
/// - parameter socket: The socket to disconnect.
|
||||||
|
func disconnectSocket(_ socket: SocketIOClient)
|
||||||
|
|
||||||
|
/// Disconnects the socket associated with `forNamespace`.
|
||||||
|
///
|
||||||
|
/// - parameter forNamespace: The namespace to disconnect from.
|
||||||
|
func disconnectSocket(forNamespace nsp: String)
|
||||||
|
|
||||||
|
/// Sends an event to the server on all namespaces in this manager.
|
||||||
|
///
|
||||||
|
/// - parameter event: The event to send.
|
||||||
|
/// - parameter withItems: The data to send with this event.
|
||||||
|
func emitAll(_ event: String, withItems items: [Any])
|
||||||
|
|
||||||
|
/// Tries to reconnect to the server.
|
||||||
|
///
|
||||||
|
/// This will cause a `disconnect` event to be emitted, as well as an `reconnectAttempt` event.
|
||||||
|
func reconnect()
|
||||||
|
|
||||||
|
/// Returns a `SocketIOClient` for the given namespace. This socket shares a transport with the manager.
|
||||||
|
///
|
||||||
|
/// Calling multiple times returns the same socket.
|
||||||
|
///
|
||||||
|
/// Sockets created from this method are retained by the manager.
|
||||||
|
/// Call one of the `disconnectSocket` methods on the implementing class to remove the socket from manager control.
|
||||||
|
/// Or call `SocketIOClient.disconnect()` on the client.
|
||||||
|
///
|
||||||
|
/// - parameter forNamespace: The namespace for the socket.
|
||||||
|
/// - returns: A `SocketIOClient` for the given namespace.
|
||||||
|
func socket(forNamespace nsp: String) -> SocketIOClient
|
||||||
|
}
|
||||||
@ -142,12 +142,8 @@ public struct SocketPacket : CustomStringConvertible {
|
|||||||
if dict["_placeholder"] as? Bool ?? false {
|
if dict["_placeholder"] as? Bool ?? false {
|
||||||
return binary[dict["num"] as! Int]
|
return binary[dict["num"] as! Int]
|
||||||
} else {
|
} else {
|
||||||
return dict.reduce(JSON(), {cur, keyValue in
|
return dict.reduce(into: JSON(), {cur, keyValue in
|
||||||
var cur = cur
|
|
||||||
|
|
||||||
cur[keyValue.0] = _fillInPlaceholders(keyValue.1)
|
cur[keyValue.0] = _fillInPlaceholders(keyValue.1)
|
||||||
|
|
||||||
return cur
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
case let arr as [Any]:
|
case let arr as [Any]:
|
||||||
@ -225,12 +221,8 @@ private extension SocketPacket {
|
|||||||
case let arr as [Any]:
|
case let arr as [Any]:
|
||||||
return arr.map({shred($0, binary: &binary)})
|
return arr.map({shred($0, binary: &binary)})
|
||||||
case let dict as JSON:
|
case let dict as JSON:
|
||||||
return dict.reduce(JSON(), {cur, keyValue in
|
return dict.reduce(into: JSON(), {cur, keyValue in
|
||||||
var mutCur = cur
|
cur[keyValue.0] = shred(keyValue.1, binary: &binary)
|
||||||
|
|
||||||
mutCur[keyValue.0] = shred(keyValue.1, binary: &binary)
|
|
||||||
|
|
||||||
return mutCur
|
|
||||||
})
|
})
|
||||||
default:
|
default:
|
||||||
return data
|
return data
|
||||||
|
|||||||
@ -26,14 +26,6 @@ import Foundation
|
|||||||
public protocol SocketParsable : class {
|
public protocol SocketParsable : class {
|
||||||
// MARK: Properties
|
// MARK: Properties
|
||||||
|
|
||||||
/// A list of packets that are waiting for binary data.
|
|
||||||
///
|
|
||||||
/// The way that socket.io works all data should be sent directly after each packet.
|
|
||||||
/// So this should ideally be an array of one packet waiting for data.
|
|
||||||
///
|
|
||||||
/// **This should not be modified directly.**
|
|
||||||
var waitingPackets: [SocketPacket] { get set }
|
|
||||||
|
|
||||||
// MARK: Methods
|
// MARK: Methods
|
||||||
|
|
||||||
/// Called when the engine has received some binary data that should be attached to a packet.
|
/// Called when the engine has received some binary data that should be attached to a packet.
|
||||||
@ -43,12 +35,13 @@ public protocol SocketParsable : class {
|
|||||||
/// into the correct placeholder.
|
/// into the correct placeholder.
|
||||||
///
|
///
|
||||||
/// - parameter data: The data that should be attached to a packet.
|
/// - parameter data: The data that should be attached to a packet.
|
||||||
func parseBinaryData(_ data: Data)
|
func parseBinaryData(_ data: Data) -> SocketPacket?
|
||||||
|
|
||||||
/// Called when the engine has received a string that should be parsed into a socket.io packet.
|
/// Called when the engine has received a string that should be parsed into a socket.io packet.
|
||||||
///
|
///
|
||||||
/// - parameter message: The string that needs parsing.
|
/// - parameter message: The string that needs parsing.
|
||||||
func parseSocketMessage(_ message: String)
|
/// - returns: A completed socket packet if there is no more data left to collect.
|
||||||
|
func parseSocketMessage(_ message: String) -> SocketPacket?
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Errors that can be thrown during parsing.
|
/// Errors that can be thrown during parsing.
|
||||||
@ -65,38 +58,18 @@ public enum SocketParsableError : Error {
|
|||||||
case invalidPacketType
|
case invalidPacketType
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension SocketParsable where Self: SocketIOClientSpec {
|
/// Says that a type will be able to buffer binary data before all data for an event has come in.
|
||||||
private func isCorrectNamespace(_ nsp: String) -> Bool {
|
public protocol SocketDataBufferable : class {
|
||||||
return nsp == self.nsp
|
/// A list of packets that are waiting for binary data.
|
||||||
}
|
///
|
||||||
|
/// The way that socket.io works all data should be sent directly after each packet.
|
||||||
private func handleConnect(_ packetNamespace: String) {
|
/// So this should ideally be an array of one packet waiting for data.
|
||||||
guard packetNamespace == nsp else { return }
|
///
|
||||||
|
/// **This should not be modified directly.**
|
||||||
didConnect(toNamespace: packetNamespace)
|
var waitingPackets: [SocketPacket] { get set }
|
||||||
}
|
|
||||||
|
|
||||||
private func handlePacket(_ pack: SocketPacket) {
|
|
||||||
switch pack.type {
|
|
||||||
case .event where isCorrectNamespace(pack.nsp):
|
|
||||||
handleEvent(pack.event, data: pack.args, isInternalMessage: false, withAck: pack.id)
|
|
||||||
case .ack where isCorrectNamespace(pack.nsp):
|
|
||||||
handleAck(pack.id, data: pack.data)
|
|
||||||
case .binaryEvent where isCorrectNamespace(pack.nsp):
|
|
||||||
waitingPackets.append(pack)
|
|
||||||
case .binaryAck where isCorrectNamespace(pack.nsp):
|
|
||||||
waitingPackets.append(pack)
|
|
||||||
case .connect:
|
|
||||||
handleConnect(pack.nsp)
|
|
||||||
case .disconnect:
|
|
||||||
didDisconnect(reason: "Got Disconnect")
|
|
||||||
case .error:
|
|
||||||
handleEvent("error", data: pack.data, isInternalMessage: true, withAck: pack.id)
|
|
||||||
default:
|
|
||||||
DefaultSocketLogger.Logger.log("Got invalid packet: \(pack.description)", type: "SocketParser")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public extension SocketParsable where Self: SocketManagerSpec & SocketDataBufferable {
|
||||||
/// Parses a message from the engine, returning a complete SocketPacket or throwing.
|
/// Parses a message from the engine, returning a complete SocketPacket or throwing.
|
||||||
///
|
///
|
||||||
/// - parameter message: The message to parse.
|
/// - parameter message: The message to parse.
|
||||||
@ -169,8 +142,9 @@ public extension SocketParsable where Self: SocketIOClientSpec {
|
|||||||
/// Called when the engine has received a string that should be parsed into a socket.io packet.
|
/// Called when the engine has received a string that should be parsed into a socket.io packet.
|
||||||
///
|
///
|
||||||
/// - parameter message: The string that needs parsing.
|
/// - parameter message: The string that needs parsing.
|
||||||
public func parseSocketMessage(_ message: String) {
|
/// - returns: A completed socket packet or nil if the packet is invalid.
|
||||||
guard !message.isEmpty else { return }
|
public func parseSocketMessage(_ message: String) -> SocketPacket? {
|
||||||
|
guard !message.isEmpty else { return nil }
|
||||||
|
|
||||||
DefaultSocketLogger.Logger.log("Parsing \(message)", type: "SocketParser")
|
DefaultSocketLogger.Logger.log("Parsing \(message)", type: "SocketParser")
|
||||||
|
|
||||||
@ -179,9 +153,11 @@ public extension SocketParsable where Self: SocketIOClientSpec {
|
|||||||
|
|
||||||
DefaultSocketLogger.Logger.log("Decoded packet as: \(packet.description)", type: "SocketParser")
|
DefaultSocketLogger.Logger.log("Decoded packet as: \(packet.description)", type: "SocketParser")
|
||||||
|
|
||||||
handlePacket(packet)
|
return packet
|
||||||
} catch {
|
} catch {
|
||||||
DefaultSocketLogger.Logger.error("\(error): \(message)", type: "SocketParser")
|
DefaultSocketLogger.Logger.error("\(error): \(message)", type: "SocketParser")
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,21 +168,17 @@ public extension SocketParsable where Self: SocketIOClientSpec {
|
|||||||
/// into the correct placeholder.
|
/// into the correct placeholder.
|
||||||
///
|
///
|
||||||
/// - parameter data: The data that should be attached to a packet.
|
/// - parameter data: The data that should be attached to a packet.
|
||||||
public func parseBinaryData(_ data: Data) {
|
/// - returns: A completed socket packet if there is no more data left to collect.
|
||||||
|
public func parseBinaryData(_ data: Data) -> SocketPacket? {
|
||||||
guard !waitingPackets.isEmpty else {
|
guard !waitingPackets.isEmpty else {
|
||||||
DefaultSocketLogger.Logger.error("Got data when not remaking packet", type: "SocketParser")
|
DefaultSocketLogger.Logger.error("Got data when not remaking packet", type: "SocketParser")
|
||||||
return
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should execute event?
|
// Should execute event?
|
||||||
guard waitingPackets[waitingPackets.count - 1].addData(data) else { return }
|
guard waitingPackets[waitingPackets.count - 1].addData(data) else { return nil }
|
||||||
|
|
||||||
let packet = waitingPackets.removeLast()
|
return waitingPackets.removeLast()
|
||||||
|
|
||||||
if packet.type != .binaryAck {
|
|
||||||
handleEvent(packet.event, data: packet.args, isInternalMessage: false, withAck: packet.id)
|
|
||||||
} else {
|
|
||||||
handleAck(packet.id, data: packet.args)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,110 +0,0 @@
|
|||||||
//
|
|
||||||
// SocketClientManager.swift
|
|
||||||
// Socket.IO-Client-Swift
|
|
||||||
//
|
|
||||||
// Created by Erik Little on 6/11/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
|
|
||||||
|
|
||||||
/**
|
|
||||||
Experimental socket manager.
|
|
||||||
|
|
||||||
API subject to change.
|
|
||||||
|
|
||||||
Can be used to persist sockets across ViewControllers.
|
|
||||||
|
|
||||||
Sockets are strongly stored, so be sure to remove them once they are no
|
|
||||||
longer needed.
|
|
||||||
|
|
||||||
Example usage:
|
|
||||||
```
|
|
||||||
let manager = SocketClientManager.sharedManager
|
|
||||||
manager["room1"] = socket1
|
|
||||||
manager["room2"] = socket2
|
|
||||||
manager.removeSocket(socket: socket2)
|
|
||||||
manager["room1"]?.emit("hello")
|
|
||||||
```
|
|
||||||
*/
|
|
||||||
open class SocketClientManager : NSObject {
|
|
||||||
// MARK: Properties.
|
|
||||||
|
|
||||||
/// The shared manager.
|
|
||||||
@objc
|
|
||||||
open static let sharedManager = SocketClientManager()
|
|
||||||
|
|
||||||
private var sockets = [String: SocketIOClient]()
|
|
||||||
|
|
||||||
/// Gets a socket by its name.
|
|
||||||
///
|
|
||||||
/// - returns: The socket, if one had the given name.
|
|
||||||
open subscript(string: String) -> SocketIOClient? {
|
|
||||||
get {
|
|
||||||
return sockets[string]
|
|
||||||
}
|
|
||||||
|
|
||||||
set(socket) {
|
|
||||||
sockets[string] = socket
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Methods.
|
|
||||||
|
|
||||||
/// Adds a socket.
|
|
||||||
///
|
|
||||||
/// - parameter socket: The socket to add.
|
|
||||||
/// - parameter labeledAs: The label for this socket.
|
|
||||||
@objc
|
|
||||||
open func addSocket(_ socket: SocketIOClient, labeledAs label: String) {
|
|
||||||
sockets[label] = socket
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes a socket by a given name.
|
|
||||||
///
|
|
||||||
/// - parameter withLabel: The label of the socket to remove.
|
|
||||||
/// - returns: The socket for the given label, if one was present.
|
|
||||||
@objc
|
|
||||||
@discardableResult
|
|
||||||
open func removeSocket(withLabel label: String) -> SocketIOClient? {
|
|
||||||
return sockets.removeValue(forKey: label)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes a socket.
|
|
||||||
///
|
|
||||||
/// - parameter socket: The socket to remove.
|
|
||||||
/// - returns: The socket if it was in the manager.
|
|
||||||
@objc
|
|
||||||
@discardableResult
|
|
||||||
open func removeSocket(_ socket: SocketIOClient) -> SocketIOClient? {
|
|
||||||
var returnSocket: SocketIOClient?
|
|
||||||
|
|
||||||
for (label, dictSocket) in sockets where dictSocket === socket {
|
|
||||||
returnSocket = sockets.removeValue(forKey: label)
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnSocket
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes all the sockets in the manager.
|
|
||||||
@objc
|
|
||||||
open func removeSockets() {
|
|
||||||
sockets.removeAll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -63,8 +63,6 @@ extension NSDictionary {
|
|||||||
return .log(log)
|
return .log(log)
|
||||||
case let ("logger", logger as SocketLogger):
|
case let ("logger", logger as SocketLogger):
|
||||||
return .logger(logger)
|
return .logger(logger)
|
||||||
case let ("nsp", nsp as String):
|
|
||||||
return .nsp(nsp)
|
|
||||||
case let ("path", path as String):
|
case let ("path", path as String):
|
||||||
return .path(path)
|
return .path(path)
|
||||||
case let ("reconnects", reconnects as Bool):
|
case let ("reconnects", reconnects as Bool):
|
||||||
|
|||||||
@ -150,10 +150,9 @@ class SocketBasicPacketTest: XCTestCase {
|
|||||||
|
|
||||||
func testBinaryStringPlaceholderInMessage() {
|
func testBinaryStringPlaceholderInMessage() {
|
||||||
let engineString = "52-[\"test\",\"~~0\",{\"num\":0,\"_placeholder\":true},{\"_placeholder\":true,\"num\":1}]"
|
let engineString = "52-[\"test\",\"~~0\",{\"num\":0,\"_placeholder\":true},{\"_placeholder\":true,\"num\":1}]"
|
||||||
let socket = SocketIOClient(socketURL: URL(string: "http://localhost/")!)
|
let manager = SocketManager(socketURL: URL(string: "http://localhost/")!)
|
||||||
socket.setTestable()
|
|
||||||
|
|
||||||
var packet = try! socket.parseString(engineString)
|
var packet = try! manager.parseString(engineString)
|
||||||
|
|
||||||
XCTAssertEqual(packet.event, "test")
|
XCTAssertEqual(packet.event, "test")
|
||||||
_ = packet.addData(data)
|
_ = packet.addData(data)
|
||||||
|
|||||||
@ -10,20 +10,9 @@ import XCTest
|
|||||||
@testable import SocketIO
|
@testable import SocketIO
|
||||||
|
|
||||||
class SocketEngineTest: XCTestCase {
|
class SocketEngineTest: XCTestCase {
|
||||||
var client: SocketIOClient!
|
|
||||||
var engine: SocketEngine!
|
|
||||||
|
|
||||||
override func setUp() {
|
|
||||||
super.setUp()
|
|
||||||
client = SocketIOClient(socketURL: URL(string: "http://localhost")!)
|
|
||||||
engine = SocketEngine(client: client, url: URL(string: "http://localhost")!, options: nil)
|
|
||||||
|
|
||||||
client.setTestable()
|
|
||||||
}
|
|
||||||
|
|
||||||
func testBasicPollingMessage() {
|
func testBasicPollingMessage() {
|
||||||
let expect = expectation(description: "Basic polling test")
|
let expect = expectation(description: "Basic polling test")
|
||||||
client.on("blankTest") {data, ack in
|
socket.on("blankTest") {data, ack in
|
||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,11 +24,11 @@ class SocketEngineTest: XCTestCase {
|
|||||||
let finalExpectation = expectation(description: "Final packet in poll test")
|
let finalExpectation = expectation(description: "Final packet in poll test")
|
||||||
var gotBlank = false
|
var gotBlank = false
|
||||||
|
|
||||||
client.on("blankTest") {data, ack in
|
socket.on("blankTest") {data, ack in
|
||||||
gotBlank = true
|
gotBlank = true
|
||||||
}
|
}
|
||||||
|
|
||||||
client.on("stringTest") {data, ack in
|
socket.on("stringTest") {data, ack in
|
||||||
if let str = data[0] as? String, gotBlank {
|
if let str = data[0] as? String, gotBlank {
|
||||||
if str == "hello" {
|
if str == "hello" {
|
||||||
finalExpectation.fulfill()
|
finalExpectation.fulfill()
|
||||||
@ -54,7 +43,7 @@ class SocketEngineTest: XCTestCase {
|
|||||||
func testEngineDoesErrorOnUnknownTransport() {
|
func testEngineDoesErrorOnUnknownTransport() {
|
||||||
let finalExpectation = expectation(description: "Unknown Transport")
|
let finalExpectation = expectation(description: "Unknown Transport")
|
||||||
|
|
||||||
client.on("error") {data, ack in
|
socket.on("error") {data, ack in
|
||||||
if let error = data[0] as? String, error == "Unknown transport" {
|
if let error = data[0] as? String, error == "Unknown transport" {
|
||||||
finalExpectation.fulfill()
|
finalExpectation.fulfill()
|
||||||
}
|
}
|
||||||
@ -67,7 +56,7 @@ class SocketEngineTest: XCTestCase {
|
|||||||
func testEngineDoesErrorOnUnknownMessage() {
|
func testEngineDoesErrorOnUnknownMessage() {
|
||||||
let finalExpectation = expectation(description: "Engine Errors")
|
let finalExpectation = expectation(description: "Engine Errors")
|
||||||
|
|
||||||
client.on("error") {data, ack in
|
socket.on("error") {data, ack in
|
||||||
finalExpectation.fulfill()
|
finalExpectation.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +67,7 @@ class SocketEngineTest: XCTestCase {
|
|||||||
func testEngineDecodesUTF8Properly() {
|
func testEngineDecodesUTF8Properly() {
|
||||||
let expect = expectation(description: "Engine Decodes utf8")
|
let expect = expectation(description: "Engine Decodes utf8")
|
||||||
|
|
||||||
client.on("stringTest") {data, ack in
|
socket.on("stringTest") {data, ack in
|
||||||
XCTAssertEqual(data[0] as? String, "lïne one\nlīne \rtwo𦅙𦅛", "Failed string test")
|
XCTAssertEqual(data[0] as? String, "lïne one\nlīne \rtwo𦅙𦅛", "Failed string test")
|
||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
}
|
}
|
||||||
@ -110,7 +99,7 @@ class SocketEngineTest: XCTestCase {
|
|||||||
let b64String = "b4aGVsbG8NCg=="
|
let b64String = "b4aGVsbG8NCg=="
|
||||||
let packetString = "451-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0}}]"
|
let packetString = "451-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0}}]"
|
||||||
|
|
||||||
client.on("test") {data, ack in
|
socket.on("test") {data, ack in
|
||||||
if let data = data[0] as? Data, let string = String(data: data, encoding: .utf8) {
|
if let data = data[0] as? Data, let string = String(data: data, encoding: .utf8) {
|
||||||
XCTAssertEqual(string, "hello")
|
XCTAssertEqual(string, "hello")
|
||||||
}
|
}
|
||||||
@ -123,4 +112,97 @@ class SocketEngineTest: XCTestCase {
|
|||||||
|
|
||||||
waitForExpectations(timeout: 3, handler: nil)
|
waitForExpectations(timeout: 3, handler: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testSettingExtraHeadersBeforeConnectSetsEngineExtraHeaders() {
|
||||||
|
let newValue = ["hello": "world"]
|
||||||
|
|
||||||
|
manager.engine = engine
|
||||||
|
manager.setTestStatus(.notConnected)
|
||||||
|
manager.config = [.extraHeaders(["new": "value"])]
|
||||||
|
manager.config.insert(.extraHeaders(newValue), replacing: true)
|
||||||
|
|
||||||
|
XCTAssertEqual(2, manager.config.count)
|
||||||
|
XCTAssertEqual(manager.engine!.extraHeaders!, newValue)
|
||||||
|
|
||||||
|
for config in manager.config {
|
||||||
|
switch config {
|
||||||
|
case let .extraHeaders(headers):
|
||||||
|
XCTAssertTrue(headers.keys.contains("hello"), "It should contain hello header key")
|
||||||
|
XCTAssertFalse(headers.keys.contains("new"), "It should not contain old data")
|
||||||
|
case .path:
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
XCTFail("It should only have two configs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSettingExtraHeadersAfterConnectDoesNotIgnoreChanges() {
|
||||||
|
let newValue = ["hello": "world"]
|
||||||
|
|
||||||
|
manager.engine = engine
|
||||||
|
manager.setTestStatus(.connected)
|
||||||
|
engine.setConnected(true)
|
||||||
|
manager.config = [.extraHeaders(["new": "value"])]
|
||||||
|
manager.config.insert(.extraHeaders(["hello": "world"]), replacing: true)
|
||||||
|
|
||||||
|
XCTAssertEqual(2, manager.config.count)
|
||||||
|
XCTAssertEqual(manager.engine!.extraHeaders!, newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSettingPathAfterConnectDoesNotIgnoreChanges() {
|
||||||
|
let newValue = "/newpath/"
|
||||||
|
|
||||||
|
manager.engine = engine
|
||||||
|
manager.setTestStatus(.connected)
|
||||||
|
engine.setConnected(true)
|
||||||
|
manager.config.insert(.path(newValue))
|
||||||
|
|
||||||
|
XCTAssertEqual(1, manager.config.count)
|
||||||
|
XCTAssertEqual(manager.engine!.socketPath, newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSettingCompressAfterConnectDoesNotIgnoreChanges() {
|
||||||
|
manager.engine = engine
|
||||||
|
manager.setTestStatus(.connected)
|
||||||
|
engine.setConnected(true)
|
||||||
|
manager.config.insert(.compress)
|
||||||
|
|
||||||
|
XCTAssertEqual(2, manager.config.count)
|
||||||
|
XCTAssertTrue(manager.engine!.compress)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSettingForcePollingAfterConnectDoesNotIgnoreChanges() {
|
||||||
|
manager.engine = engine
|
||||||
|
manager.setTestStatus(.connected)
|
||||||
|
engine.setConnected(true)
|
||||||
|
manager.config.insert(.forcePolling(true))
|
||||||
|
|
||||||
|
XCTAssertEqual(2, manager.config.count)
|
||||||
|
XCTAssertTrue(manager.engine!.forcePolling)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSettingForceWebSocketsAfterConnectDoesNotIgnoreChanges() {
|
||||||
|
manager.engine = engine
|
||||||
|
manager.setTestStatus(.connected)
|
||||||
|
engine.setConnected(true)
|
||||||
|
manager.config.insert(.forceWebsockets(true))
|
||||||
|
|
||||||
|
XCTAssertEqual(2, manager.config.count)
|
||||||
|
XCTAssertTrue(manager.engine!.forceWebsockets)
|
||||||
|
}
|
||||||
|
|
||||||
|
var manager: SocketManager!
|
||||||
|
var socket: SocketIOClient!
|
||||||
|
var engine: SocketEngine!
|
||||||
|
|
||||||
|
override func setUp() {
|
||||||
|
super.setUp()
|
||||||
|
|
||||||
|
manager = SocketManager(socketURL: URL(string: "http://localhost")!)
|
||||||
|
socket = manager.defaultSocket
|
||||||
|
engine = SocketEngine(client: manager, url: URL(string: "http://localhost")!, options: nil)
|
||||||
|
|
||||||
|
socket.setTestable()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,14 +10,6 @@ import XCTest
|
|||||||
import SocketIO
|
import SocketIO
|
||||||
|
|
||||||
class TestSocketIOClientConfiguration : XCTestCase {
|
class TestSocketIOClientConfiguration : XCTestCase {
|
||||||
var config = [] as SocketIOClientConfiguration
|
|
||||||
|
|
||||||
override func setUp() {
|
|
||||||
super.setUp()
|
|
||||||
|
|
||||||
config = [.log(false), .forceNew(true)]
|
|
||||||
}
|
|
||||||
|
|
||||||
func testReplaceSameOption() {
|
func testReplaceSameOption() {
|
||||||
config.insert(.log(true))
|
config.insert(.log(true))
|
||||||
|
|
||||||
@ -43,4 +35,12 @@ class TestSocketIOClientConfiguration: XCTestCase {
|
|||||||
XCTFail()
|
XCTFail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var config = [] as SocketIOClientConfiguration
|
||||||
|
|
||||||
|
override func setUp() {
|
||||||
|
config = [.log(false), .forceNew(true)]
|
||||||
|
|
||||||
|
super.setUp()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
174
Tests/TestSocketIO/SocketMangerTest.swift
Normal file
174
Tests/TestSocketIO/SocketMangerTest.swift
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
//
|
||||||
|
// Created by Erik Little on 10/21/17.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Dispatch
|
||||||
|
import Foundation
|
||||||
|
@testable import SocketIO
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
class SocketMangerTest : XCTestCase {
|
||||||
|
func testManagerProperties() {
|
||||||
|
XCTAssertNotNil(manager.defaultSocket)
|
||||||
|
XCTAssertNil(manager.engine)
|
||||||
|
XCTAssertFalse(manager.forceNew)
|
||||||
|
XCTAssertEqual(manager.handleQueue, DispatchQueue.main)
|
||||||
|
XCTAssertTrue(manager.reconnects)
|
||||||
|
XCTAssertEqual(manager.reconnectWait, 10)
|
||||||
|
XCTAssertEqual(manager.status, .notConnected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testManagerCallsConnect() {
|
||||||
|
setUpSockets()
|
||||||
|
|
||||||
|
socket.expectations[ManagerExpectation.didConnectCalled] = expectation(description: "The manager should call connect on the default socket")
|
||||||
|
socket2.expectations[ManagerExpectation.didConnectCalled] = expectation(description: "The manager should call connect on the socket")
|
||||||
|
|
||||||
|
socket.connect()
|
||||||
|
socket2.connect()
|
||||||
|
|
||||||
|
manager.fakeConnecting()
|
||||||
|
manager.fakeConnecting(toNamespace: "/swift")
|
||||||
|
|
||||||
|
waitForExpectations(timeout: 0.3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testManagerCallsDisconnect() {
|
||||||
|
setUpSockets()
|
||||||
|
|
||||||
|
socket.expectations[ManagerExpectation.didDisconnectCalled] = expectation(description: "The manager should call disconnect on the default socket")
|
||||||
|
socket2.expectations[ManagerExpectation.didDisconnectCalled] = expectation(description: "The manager should call disconnect on the socket")
|
||||||
|
|
||||||
|
socket2.on(clientEvent: .connect) {data, ack in
|
||||||
|
self.manager.disconnect()
|
||||||
|
self.manager.fakeDisconnecting()
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.connect()
|
||||||
|
socket2.connect()
|
||||||
|
|
||||||
|
manager.fakeConnecting()
|
||||||
|
manager.fakeConnecting(toNamespace: "/swift")
|
||||||
|
|
||||||
|
waitForExpectations(timeout: 0.3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testManagerEmitAll() {
|
||||||
|
setUpSockets()
|
||||||
|
|
||||||
|
socket.expectations[ManagerExpectation.emitAllEventCalled] = expectation(description: "The manager should emit an event to the default socket")
|
||||||
|
socket2.expectations[ManagerExpectation.emitAllEventCalled] = expectation(description: "The manager should emit an event to the socket")
|
||||||
|
|
||||||
|
socket2.on(clientEvent: .connect) {data, ack in
|
||||||
|
self.manager.emitAll("event", "testing")
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.connect()
|
||||||
|
socket2.connect()
|
||||||
|
|
||||||
|
manager.fakeConnecting()
|
||||||
|
manager.fakeConnecting(toNamespace: "/swift")
|
||||||
|
|
||||||
|
waitForExpectations(timeout: 0.3)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setUpSockets() {
|
||||||
|
socket = manager.testSocket(forNamespace: "/")
|
||||||
|
socket2 = manager.testSocket(forNamespace: "/swift")
|
||||||
|
}
|
||||||
|
|
||||||
|
private var manager: TestManager!
|
||||||
|
private var socket: TestSocket!
|
||||||
|
private var socket2: TestSocket!
|
||||||
|
|
||||||
|
override func setUp() {
|
||||||
|
super.setUp()
|
||||||
|
|
||||||
|
manager = TestManager(socketURL: URL(string: "http://localhost/")!)
|
||||||
|
socket = nil
|
||||||
|
socket2 = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ManagerExpectation : String {
|
||||||
|
case didConnectCalled
|
||||||
|
case didDisconnectCalled
|
||||||
|
case emitAllEventCalled
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestManager : SocketManager {
|
||||||
|
public override func disconnect() {
|
||||||
|
setTestStatus(.disconnected)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
public func testSocket(forNamespace nsp: String) -> TestSocket {
|
||||||
|
return socket(forNamespace: nsp) as! TestSocket
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
public func fakeConnecting(toNamespace nsp: String) {
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
|
||||||
|
// Fake connecting
|
||||||
|
self.parseEngineMessage("0\(nsp)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
public func fakeDisconnecting() {
|
||||||
|
engineDidClose(reason: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
public func fakeConnecting() {
|
||||||
|
engineDidOpen(reason: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
public override func socket(forNamespace nsp: String) -> SocketIOClient {
|
||||||
|
// set socket to our test socket, the superclass method will get this from nsps
|
||||||
|
nsps[nsp] = TestSocket(manager: self, nsp: nsp)
|
||||||
|
|
||||||
|
return super.socket(forNamespace: nsp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestSocket : SocketIOClient {
|
||||||
|
public var expectations = [ManagerExpectation: XCTestExpectation]()
|
||||||
|
|
||||||
|
@objc
|
||||||
|
public var expects = NSMutableDictionary()
|
||||||
|
|
||||||
|
public override func didConnect(toNamespace nsp: String) {
|
||||||
|
expectations[ManagerExpectation.didConnectCalled]?.fulfill()
|
||||||
|
expectations[ManagerExpectation.didConnectCalled] = nil
|
||||||
|
|
||||||
|
if let expect = expects[ManagerExpectation.didConnectCalled.rawValue] as? XCTestExpectation {
|
||||||
|
expect.fulfill()
|
||||||
|
expects[ManagerExpectation.didConnectCalled.rawValue] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
super.didConnect(toNamespace: nsp)
|
||||||
|
}
|
||||||
|
|
||||||
|
public override func didDisconnect(reason: String) {
|
||||||
|
expectations[ManagerExpectation.didDisconnectCalled]?.fulfill()
|
||||||
|
expectations[ManagerExpectation.didDisconnectCalled] = nil
|
||||||
|
|
||||||
|
if let expect = expects[ManagerExpectation.didDisconnectCalled.rawValue] as? XCTestExpectation {
|
||||||
|
expect.fulfill()
|
||||||
|
expects[ManagerExpectation.didDisconnectCalled.rawValue] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
super.didDisconnect(reason: reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
public override func emit(_ event: String, with items: [Any]) {
|
||||||
|
expectations[ManagerExpectation.emitAllEventCalled]?.fulfill()
|
||||||
|
expectations[ManagerExpectation.emitAllEventCalled] = nil
|
||||||
|
|
||||||
|
if let expect = expects[ManagerExpectation.emitAllEventCalled.rawValue] as? XCTestExpectation {
|
||||||
|
expect.fulfill()
|
||||||
|
expects[ManagerExpectation.emitAllEventCalled.rawValue] = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,26 +10,6 @@ import XCTest
|
|||||||
@testable import SocketIO
|
@testable import SocketIO
|
||||||
|
|
||||||
class SocketParserTest: XCTestCase {
|
class SocketParserTest: XCTestCase {
|
||||||
let testSocket = SocketIOClient(socketURL: URL(string: "http://localhost/")!)
|
|
||||||
|
|
||||||
//Format key: message; namespace-data-binary-id
|
|
||||||
static let packetTypes: [String: (String, [Any], [Data], Int)] = [
|
|
||||||
"0": ("/", [], [], -1), "1": ("/", [], [], -1),
|
|
||||||
"25[\"test\"]": ("/", ["test"], [], 5),
|
|
||||||
"2[\"test\",\"~~0\"]": ("/", ["test", "~~0"], [], -1),
|
|
||||||
"2/swift,[\"testArrayEmitReturn\",[\"test3\",\"test4\"]]": ("/swift", ["testArrayEmitReturn", ["test3", "test4"] as NSArray], [], -1),
|
|
||||||
"51-/swift,[\"testMultipleItemsWithBufferEmitReturn\",[1,2],{\"test\":\"bob\"},25,\"polo\",{\"_placeholder\":true,\"num\":0}]": ("/swift", ["testMultipleItemsWithBufferEmitReturn", [1, 2] as NSArray, ["test": "bob"] as NSDictionary, 25, "polo", ["_placeholder": true, "num": 0] as NSDictionary], [], -1),
|
|
||||||
"3/swift,0[[\"test3\",\"test4\"]]": ("/swift", [["test3", "test4"] as NSArray], [], 0),
|
|
||||||
"61-/swift,19[[1,2],{\"test\":\"bob\"},25,\"polo\",{\"_placeholder\":true,\"num\":0}]":
|
|
||||||
("/swift", [ [1, 2] as NSArray, ["test": "bob"] as NSDictionary, 25, "polo", ["_placeholder": true, "num": 0] as NSDictionary], [], 19),
|
|
||||||
"4/swift,": ("/swift", [], [], -1),
|
|
||||||
"0/swift": ("/swift", [], [], -1),
|
|
||||||
"1/swift": ("/swift", [], [], -1),
|
|
||||||
"4\"ERROR\"": ("/", ["ERROR"], [], -1),
|
|
||||||
"4{\"test\":2}": ("/", [["test": 2]], [], -1),
|
|
||||||
"41": ("/", [1], [], -1),
|
|
||||||
"4[1, \"hello\"]": ("/", [1, "hello"], [], -1)]
|
|
||||||
|
|
||||||
func testDisconnect() {
|
func testDisconnect() {
|
||||||
let message = "1"
|
let message = "1"
|
||||||
validateParseResult(message)
|
validateParseResult(message)
|
||||||
@ -108,7 +88,7 @@ class SocketParserTest: XCTestCase {
|
|||||||
func testInvalidInput() {
|
func testInvalidInput() {
|
||||||
let message = "8"
|
let message = "8"
|
||||||
do {
|
do {
|
||||||
let _ = try testSocket.parseString(message)
|
let _ = try testManager.parseString(message)
|
||||||
XCTFail()
|
XCTFail()
|
||||||
} catch {
|
} catch {
|
||||||
|
|
||||||
@ -125,7 +105,7 @@ class SocketParserTest: XCTestCase {
|
|||||||
|
|
||||||
func validateParseResult(_ message: String) {
|
func validateParseResult(_ message: String) {
|
||||||
let validValues = SocketParserTest.packetTypes[message]!
|
let validValues = SocketParserTest.packetTypes[message]!
|
||||||
let packet = try! testSocket.parseString(message)
|
let packet = try! testManager.parseString(message)
|
||||||
let type = String(message.characters.prefix(1))
|
let type = String(message.characters.prefix(1))
|
||||||
|
|
||||||
XCTAssertEqual(packet.type, SocketPacket.PacketType(rawValue: Int(type) ?? -1)!)
|
XCTAssertEqual(packet.type, SocketPacket.PacketType(rawValue: Int(type) ?? -1)!)
|
||||||
@ -139,8 +119,29 @@ class SocketParserTest: XCTestCase {
|
|||||||
let keys = Array(SocketParserTest.packetTypes.keys)
|
let keys = Array(SocketParserTest.packetTypes.keys)
|
||||||
measure {
|
measure {
|
||||||
for item in keys.enumerated() {
|
for item in keys.enumerated() {
|
||||||
_ = try! self.testSocket.parseString(item.element)
|
_ = try! self.testManager.parseString(item.element)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let testManager = SocketManager(socketURL: URL(string: "http://localhost/")!)
|
||||||
|
|
||||||
|
//Format key: message; namespace-data-binary-id
|
||||||
|
static let packetTypes: [String: (String, [Any], [Data], Int)] = [
|
||||||
|
"0": ("/", [], [], -1), "1": ("/", [], [], -1),
|
||||||
|
"25[\"test\"]": ("/", ["test"], [], 5),
|
||||||
|
"2[\"test\",\"~~0\"]": ("/", ["test", "~~0"], [], -1),
|
||||||
|
"2/swift,[\"testArrayEmitReturn\",[\"test3\",\"test4\"]]": ("/swift", ["testArrayEmitReturn", ["test3", "test4"] as NSArray], [], -1),
|
||||||
|
"51-/swift,[\"testMultipleItemsWithBufferEmitReturn\",[1,2],{\"test\":\"bob\"},25,\"polo\",{\"_placeholder\":true,\"num\":0}]": ("/swift", ["testMultipleItemsWithBufferEmitReturn", [1, 2] as NSArray, ["test": "bob"] as NSDictionary, 25, "polo", ["_placeholder": true, "num": 0] as NSDictionary], [], -1),
|
||||||
|
"3/swift,0[[\"test3\",\"test4\"]]": ("/swift", [["test3", "test4"] as NSArray], [], 0),
|
||||||
|
"61-/swift,19[[1,2],{\"test\":\"bob\"},25,\"polo\",{\"_placeholder\":true,\"num\":0}]":
|
||||||
|
("/swift", [ [1, 2] as NSArray, ["test": "bob"] as NSDictionary, 25, "polo", ["_placeholder": true, "num": 0] as NSDictionary], [], 19),
|
||||||
|
"4/swift,": ("/swift", [], [], -1),
|
||||||
|
"0/swift": ("/swift", [], [], -1),
|
||||||
|
"1/swift": ("/swift", [], [], -1),
|
||||||
|
"4\"ERROR\"": ("/", ["ERROR"], [], -1),
|
||||||
|
"4{\"test\":2}": ("/", [["test": 2]], [], -1),
|
||||||
|
"41": ("/", [1], [], -1),
|
||||||
|
"4[1, \"hello\"]": ("/", [1, "hello"], [], -1)
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,7 +34,7 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.parseSocketMessage("30[\"hello world\"]")
|
manager.parseEngineMessage("30[\"hello world\"]")
|
||||||
waitForExpectations(timeout: 3, handler: nil)
|
waitForExpectations(timeout: 3, handler: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,8 +45,8 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.parseSocketMessage("61-0[{\"_placeholder\":true,\"num\":0},{\"test\":true}]")
|
manager.parseEngineMessage("61-0[{\"_placeholder\":true,\"num\":0},{\"test\":true}]")
|
||||||
socket.parseBinaryData(Data())
|
manager.parseEngineBinaryData(Data())
|
||||||
waitForExpectations(timeout: 3, handler: nil)
|
waitForExpectations(timeout: 3, handler: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.parseSocketMessage("2[\"test\",\"hello world\"]")
|
manager.parseEngineMessage("2[\"test\",\"hello world\"]")
|
||||||
waitForExpectations(timeout: 3, handler: nil)
|
waitForExpectations(timeout: 3, handler: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.parseSocketMessage("2[\"test\",\"\\\"hello world\\\"\"]")
|
manager.parseEngineMessage("2[\"test\",\"\\\"hello world\\\"\"]")
|
||||||
waitForExpectations(timeout: 3, handler: nil)
|
waitForExpectations(timeout: 3, handler: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.parseSocketMessage("2[\"test\",\"hello world\"]")
|
manager.parseEngineMessage("2[\"test\",\"hello world\"]")
|
||||||
waitForExpectations(timeout: 3, handler: nil)
|
waitForExpectations(timeout: 3, handler: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
|
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
|
||||||
// Fake connecting
|
// Fake connecting
|
||||||
self.socket.parseEngineMessage("0/")
|
self.manager.parseEngineMessage("0/")
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForExpectations(timeout: 3, handler: nil)
|
waitForExpectations(timeout: 3, handler: nil)
|
||||||
@ -136,59 +136,47 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.parseSocketMessage("4\"test error\"")
|
manager.parseEngineMessage("4\"test error\"")
|
||||||
waitForExpectations(timeout: 3, handler: nil)
|
waitForExpectations(timeout: 3, handler: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testHandleBinaryEvent() {
|
func testHandleBinaryEvent() {
|
||||||
let expect = expectation(description: "handled binary event")
|
let expect = expectation(description: "handled binary event")
|
||||||
socket.on("test") {data, ack in
|
socket.on("test") {data, ack in
|
||||||
if let dict = data[0] as? NSDictionary, let data = dict["test"] as? NSData {
|
if let dict = data[0] as? [String: Any], let data = dict["test"] as? Data {
|
||||||
XCTAssertEqual(data as Data, self.data)
|
XCTAssertEqual(data as Data, self.data)
|
||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.parseSocketMessage("51-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0}}]")
|
manager.parseEngineMessage("51-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0}}]")
|
||||||
socket.parseBinaryData(data)
|
manager.parseEngineBinaryData(data)
|
||||||
waitForExpectations(timeout: 3, handler: nil)
|
waitForExpectations(timeout: 3, handler: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testHandleMultipleBinaryEvent() {
|
func testHandleMultipleBinaryEvent() {
|
||||||
let expect = expectation(description: "handled multiple binary event")
|
let expect = expectation(description: "handled multiple binary event")
|
||||||
socket.on("test") {data, ack in
|
socket.on("test") {data, ack in
|
||||||
if let dict = data[0] as? NSDictionary, let data = dict["test"] as? NSData,
|
if let dict = data[0] as? [String: Any], let data = dict["test"] as? Data,
|
||||||
let data2 = dict["test2"] as? NSData {
|
let data2 = dict["test2"] as? Data {
|
||||||
XCTAssertEqual(data as Data, self.data)
|
XCTAssertEqual(data as Data, self.data)
|
||||||
XCTAssertEqual(data2 as Data, self.data2)
|
XCTAssertEqual(data2 as Data, self.data2)
|
||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.parseSocketMessage("52-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0},\"test2\":{\"_placeholder\":true,\"num\":1}}]")
|
manager.parseEngineMessage("52-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0},\"test2\":{\"_placeholder\":true,\"num\":1}}]")
|
||||||
socket.parseBinaryData(data)
|
manager.parseEngineBinaryData(data)
|
||||||
socket.parseBinaryData(data2)
|
manager.parseEngineBinaryData(data2)
|
||||||
waitForExpectations(timeout: 3, handler: nil)
|
waitForExpectations(timeout: 3, handler: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSocketManager() {
|
|
||||||
let manager = SocketClientManager.sharedManager
|
|
||||||
manager["test"] = socket
|
|
||||||
|
|
||||||
XCTAssert(manager["test"] === socket, "failed to get socket")
|
|
||||||
|
|
||||||
manager["test"] = nil
|
|
||||||
|
|
||||||
XCTAssert(manager["test"] == nil, "socket not removed")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func testChangingStatusCallsStatusChangeHandler() {
|
func testChangingStatusCallsStatusChangeHandler() {
|
||||||
let expect = expectation(description: "The client should announce when the status changes")
|
let expect = expectation(description: "The client should announce when the status changes")
|
||||||
let statusChange = SocketIOClientStatus.connecting
|
let statusChange = SocketIOStatus.connecting
|
||||||
|
|
||||||
socket.on("statusChange") {data, ack in
|
socket.on("statusChange") {data, ack in
|
||||||
guard let status = data[0] as? SocketIOClientStatus else {
|
guard let status = data[0] as? SocketIOStatus else {
|
||||||
XCTFail("Status should be one of the defined statuses")
|
XCTFail("Status should be one of the defined statuses")
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -251,9 +239,9 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
func testConnectTimesOutIfNotConnected() {
|
func testConnectTimesOutIfNotConnected() {
|
||||||
let expect = expectation(description: "The client should call the timeout function")
|
let expect = expectation(description: "The client should call the timeout function")
|
||||||
|
|
||||||
|
socket = manager.socket(forNamespace: "/someNamespace")
|
||||||
socket.setTestStatus(.notConnected)
|
socket.setTestStatus(.notConnected)
|
||||||
socket.nsp = "/someNamespace"
|
manager.engine = TestEngine(client: manager, url: manager.socketURL, options: nil)
|
||||||
socket.engine = TestEngine(client: socket, url: socket.socketURL, options: nil)
|
|
||||||
|
|
||||||
socket.connect(timeoutAfter: 0.5, withHandler: {
|
socket.connect(timeoutAfter: 0.5, withHandler: {
|
||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
@ -266,7 +254,7 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
let expect = expectation(description: "The client should not call the timeout function")
|
let expect = expectation(description: "The client should not call the timeout function")
|
||||||
|
|
||||||
socket.setTestStatus(.notConnected)
|
socket.setTestStatus(.notConnected)
|
||||||
socket.engine = TestEngine(client: socket, url: socket.socketURL, options: nil)
|
manager.engine = TestEngine(client: manager, url: manager.socketURL, options: nil)
|
||||||
|
|
||||||
socket.on(clientEvent: .connect) {data, ack in
|
socket.on(clientEvent: .connect) {data, ack in
|
||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
@ -278,7 +266,7 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
|
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
|
||||||
// Fake connecting
|
// Fake connecting
|
||||||
self.socket.parseEngineMessage("0/")
|
self.manager.parseEngineMessage("0/")
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForExpectations(timeout: 2)
|
waitForExpectations(timeout: 2)
|
||||||
@ -288,7 +276,7 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
let expect = expectation(description: "The client call the connect handler")
|
let expect = expectation(description: "The client call the connect handler")
|
||||||
|
|
||||||
socket.setTestStatus(.notConnected)
|
socket.setTestStatus(.notConnected)
|
||||||
socket.engine = TestEngine(client: socket, url: socket.socketURL, options: nil)
|
manager.engine = TestEngine(client: manager, url: manager.socketURL, options: nil)
|
||||||
|
|
||||||
socket.on(clientEvent: .connect) {data, ack in
|
socket.on(clientEvent: .connect) {data, ack in
|
||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
@ -305,9 +293,9 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
let expect = expectation(description: "The client should not call the timeout function")
|
let expect = expectation(description: "The client should not call the timeout function")
|
||||||
let nspString = "/swift"
|
let nspString = "/swift"
|
||||||
|
|
||||||
|
socket = manager.socket(forNamespace: "/swift")
|
||||||
socket.setTestStatus(.notConnected)
|
socket.setTestStatus(.notConnected)
|
||||||
socket.nsp = nspString
|
manager.engine = TestEngine(client: manager, url: manager.socketURL, options: nil)
|
||||||
socket.engine = TestEngine(client: socket, url: socket.socketURL, options: nil)
|
|
||||||
|
|
||||||
socket.on(clientEvent: .connect) {data, ack in
|
socket.on(clientEvent: .connect) {data, ack in
|
||||||
guard let nsp = data[0] as? String else {
|
guard let nsp = data[0] as? String else {
|
||||||
@ -327,7 +315,7 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
|
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
|
||||||
// Fake connecting
|
// Fake connecting
|
||||||
self.socket.parseEngineMessage("0/swift")
|
self.manager.parseEngineMessage("0/swift")
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForExpectations(timeout: 2)
|
waitForExpectations(timeout: 2)
|
||||||
@ -377,39 +365,31 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
|
|
||||||
func testSettingConfigAfterInit() {
|
func testSettingConfigAfterInit() {
|
||||||
socket.setTestStatus(.notConnected)
|
socket.setTestStatus(.notConnected)
|
||||||
socket.config.insert(.log(true))
|
manager.config.insert(.log(true))
|
||||||
|
|
||||||
XCTAssertTrue(DefaultSocketLogger.Logger.log, "It should set logging to true after creation")
|
XCTAssertTrue(DefaultSocketLogger.Logger.log, "It should set logging to true after creation")
|
||||||
|
|
||||||
socket.config = [.log(false), .nsp("/test")]
|
manager.config = [.log(false)]
|
||||||
|
|
||||||
XCTAssertFalse(DefaultSocketLogger.Logger.log, "It should set logging to false after creation")
|
XCTAssertFalse(DefaultSocketLogger.Logger.log, "It should set logging to false after creation")
|
||||||
XCTAssertEqual(socket.nsp, "/test", "It should set the namespace after creation")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSettingExtraHeadersAfterInit() {
|
func testSettingConfigAfterDisconnect() {
|
||||||
socket.setTestStatus(.notConnected)
|
socket.setTestStatus(.disconnected)
|
||||||
socket.config = [.extraHeaders(["new": "value"])]
|
manager.config.insert(.log(true))
|
||||||
socket.config.insert(.extraHeaders(["hello": "world"]), replacing: true)
|
|
||||||
|
|
||||||
for config in socket.config {
|
XCTAssertTrue(DefaultSocketLogger.Logger.log, "It should set logging to true after creation")
|
||||||
switch config {
|
|
||||||
case let .extraHeaders(headers):
|
|
||||||
XCTAssertTrue(headers.keys.contains("hello"), "It should contain hello header key")
|
|
||||||
XCTAssertFalse(headers.keys.contains("new"), "It should not contain old data")
|
|
||||||
case .path:
|
|
||||||
continue
|
|
||||||
default:
|
|
||||||
XCTFail("It should only have two configs")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testSettingConfigAfterInitWhenConnectedIgnoresChanges() {
|
manager.config = [.log(false)]
|
||||||
socket.config = [.log(true), .nsp("/test")]
|
|
||||||
|
|
||||||
XCTAssertFalse(DefaultSocketLogger.Logger.log, "It should set logging to false after creation")
|
XCTAssertFalse(DefaultSocketLogger.Logger.log, "It should set logging to false after creation")
|
||||||
XCTAssertEqual(socket.nsp, "/", "It should set the namespace after creation")
|
}
|
||||||
|
|
||||||
|
func testSettingConfigAfterInitWhenConnectedDoesNotIgnoreChanges() {
|
||||||
|
manager.connect()
|
||||||
|
manager.config = [.log(true)]
|
||||||
|
|
||||||
|
XCTAssertTrue(DefaultSocketLogger.Logger.log, "It should set logging to false after creation")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testClientCallsSentPingHandler() {
|
func testClientCallsSentPingHandler() {
|
||||||
@ -419,7 +399,7 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.engineDidSendPing()
|
manager.engineDidSendPing()
|
||||||
|
|
||||||
waitForExpectations(timeout: 0.2)
|
waitForExpectations(timeout: 0.2)
|
||||||
}
|
}
|
||||||
@ -431,18 +411,22 @@ class SocketSideEffectTest: XCTestCase {
|
|||||||
expect.fulfill()
|
expect.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.engineDidReceivePong()
|
manager.engineDidReceivePong()
|
||||||
|
|
||||||
waitForExpectations(timeout: 0.2)
|
waitForExpectations(timeout: 0.2)
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = "test".data(using: String.Encoding.utf8)!
|
let data = "test".data(using: String.Encoding.utf8)!
|
||||||
let data2 = "test2".data(using: String.Encoding.utf8)!
|
let data2 = "test2".data(using: String.Encoding.utf8)!
|
||||||
|
|
||||||
|
private var manager: SocketManager!
|
||||||
private var socket: SocketIOClient!
|
private var socket: SocketIOClient!
|
||||||
|
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
super.setUp()
|
super.setUp()
|
||||||
socket = SocketIOClient(socketURL: URL(string: "http://localhost/")!)
|
|
||||||
|
manager = SocketManager(socketURL: URL(string: "http://localhost/")!)
|
||||||
|
socket = manager.defaultSocket
|
||||||
socket.setTestable()
|
socket.setTestable()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -461,6 +445,7 @@ struct ThrowingData : SocketData {
|
|||||||
class TestEngine : SocketEngineSpec {
|
class TestEngine : SocketEngineSpec {
|
||||||
weak var client: SocketEngineClient?
|
weak var client: SocketEngineClient?
|
||||||
private(set) var closed = false
|
private(set) var closed = false
|
||||||
|
private(set) var compress = false
|
||||||
private(set) var connected = false
|
private(set) var connected = false
|
||||||
var connectParams: [String: Any]? = nil
|
var connectParams: [String: Any]? = nil
|
||||||
private(set) var cookies: [HTTPCookie]? = nil
|
private(set) var cookies: [HTTPCookie]? = nil
|
||||||
|
|||||||
16
Tests/TestSocketIOObjc/ManagerObjectiveCTest.h
Normal file
16
Tests/TestSocketIOObjc/ManagerObjectiveCTest.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// Created by Erik Little on 10/21/17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "SocketIO_Tests-Swift.h"
|
||||||
|
|
||||||
|
@import XCTest;
|
||||||
|
@import SocketIO;
|
||||||
|
|
||||||
|
@interface ManagerObjectiveCTest : XCTestCase
|
||||||
|
|
||||||
|
@property TestSocket* socket;
|
||||||
|
@property TestSocket* socket2;
|
||||||
|
@property TestManager* manager;
|
||||||
|
|
||||||
|
@end
|
||||||
115
Tests/TestSocketIOObjc/ManagerObjectiveCTest.m
Normal file
115
Tests/TestSocketIOObjc/ManagerObjectiveCTest.m
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
//
|
||||||
|
// Created by Erik Little on 10/21/17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "ManagerObjectiveCTest.h"
|
||||||
|
|
||||||
|
@import Dispatch;
|
||||||
|
@import Foundation;
|
||||||
|
@import XCTest;
|
||||||
|
@import SocketIO;
|
||||||
|
|
||||||
|
@implementation ManagerObjectiveCTest
|
||||||
|
|
||||||
|
- (void)testManagerProperties {
|
||||||
|
XCTAssertNotNil(self.manager.defaultSocket);
|
||||||
|
XCTAssertNil(self.manager.engine);
|
||||||
|
XCTAssertFalse(self.manager.forceNew);
|
||||||
|
XCTAssertEqual(self.manager.handleQueue, dispatch_get_main_queue());
|
||||||
|
XCTAssertTrue(self.manager.reconnects);
|
||||||
|
XCTAssertEqual(self.manager.reconnectWait, 10);
|
||||||
|
XCTAssertEqual(self.manager.status, SocketIOStatusNotConnected);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testConnectSocketSyntax {
|
||||||
|
[self setUpSockets];
|
||||||
|
[self.manager connectSocket:self.socket];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testDisconnectSocketSyntax {
|
||||||
|
[self setUpSockets];
|
||||||
|
[self.manager disconnectSocket:self.socket];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testSocketForNamespaceSyntax {
|
||||||
|
SocketIOClient* client = [self.manager socketForNamespace:@"/swift"];
|
||||||
|
client = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testManagerCallsConnect {
|
||||||
|
[self setUpSockets];
|
||||||
|
|
||||||
|
XCTestExpectation* expect = [self expectationWithDescription:@"The manager should call connect on the default socket"];
|
||||||
|
XCTestExpectation* expect2 = [self expectationWithDescription:@"The manager should call connect on the socket"];
|
||||||
|
|
||||||
|
self.socket.expects[@"didConnectCalled"] = expect;
|
||||||
|
self.socket2.expects[@"didConnectCalled"] = expect2;
|
||||||
|
|
||||||
|
[self.socket connect];
|
||||||
|
[self.socket2 connect];
|
||||||
|
|
||||||
|
[self.manager fakeConnecting];
|
||||||
|
[self.manager fakeConnectingToNamespace:@"/swift"];
|
||||||
|
|
||||||
|
[self waitForExpectationsWithTimeout:0.3 handler:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testManagerCallsDisconnect {
|
||||||
|
[self setUpSockets];
|
||||||
|
|
||||||
|
XCTestExpectation* expect = [self expectationWithDescription:@"The manager should call disconnect on the default socket"];
|
||||||
|
XCTestExpectation* expect2 = [self expectationWithDescription:@"The manager should call disconnect on the socket"];
|
||||||
|
|
||||||
|
self.socket.expects[@"didDisconnectCalled"] = expect;
|
||||||
|
self.socket2.expects[@"didDisconnectCalled"] = expect2;
|
||||||
|
|
||||||
|
[self.socket2 on:@"connect" callback:^(NSArray* data, SocketAckEmitter* ack) {
|
||||||
|
[self.manager disconnect];
|
||||||
|
[self.manager fakeDisconnecting];
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self.socket connect];
|
||||||
|
[self.socket2 connect];
|
||||||
|
|
||||||
|
[self.manager fakeConnecting];
|
||||||
|
[self.manager fakeConnectingToNamespace:@"/swift"];
|
||||||
|
|
||||||
|
[self waitForExpectationsWithTimeout:0.3 handler:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testManagerEmitAll {
|
||||||
|
[self setUpSockets];
|
||||||
|
|
||||||
|
XCTestExpectation* expect = [self expectationWithDescription:@"The manager should emit an event to the default socket"];
|
||||||
|
XCTestExpectation* expect2 = [self expectationWithDescription:@"The manager should emit an event to the socket"];
|
||||||
|
|
||||||
|
self.socket.expects[@"emitAllEventCalled"] = expect;
|
||||||
|
self.socket2.expects[@"emitAllEventCalled"] = expect2;
|
||||||
|
|
||||||
|
[self.socket2 on:@"connect" callback:^(NSArray* data, SocketAckEmitter* ack) {
|
||||||
|
[self.manager emitAll:@"event" withItems:@[@"testing"]];
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self.socket connect];
|
||||||
|
[self.socket2 connect];
|
||||||
|
|
||||||
|
[self.manager fakeConnecting];
|
||||||
|
[self.manager fakeConnectingToNamespace:@"/swift"];
|
||||||
|
|
||||||
|
[self waitForExpectationsWithTimeout:0.3 handler:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setUpSockets {
|
||||||
|
self.socket = [self.manager testSocketForNamespace:@"/"];
|
||||||
|
self.socket2 = [self.manager testSocketForNamespace:@"/swift"];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setUp {
|
||||||
|
[super setUp];
|
||||||
|
NSURL* url = [[NSURL alloc] initWithString:@"http://localhost"];
|
||||||
|
self.manager = [[TestManager alloc] initWithSocketURL:url config:nil];
|
||||||
|
self.socket = nil;
|
||||||
|
self.socket2 = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
16
Tests/TestSocketIOObjc/SocketObjectiveCTest.h
Normal file
16
Tests/TestSocketIOObjc/SocketObjectiveCTest.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// Created by Erik Little on 10/21/17.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
@import Dispatch;
|
||||||
|
@import Foundation;
|
||||||
|
@import XCTest;
|
||||||
|
@import SocketIO;
|
||||||
|
|
||||||
|
@interface SocketObjectiveCTest : XCTestCase
|
||||||
|
|
||||||
|
@property SocketIOClient* socket;
|
||||||
|
@property SocketManager* manager;
|
||||||
|
|
||||||
|
@end
|
||||||
@ -7,36 +7,20 @@
|
|||||||
// Merely tests whether the Objective-C api breaks
|
// Merely tests whether the Objective-C api breaks
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#import "SocketObjectiveCTest.h"
|
||||||
|
|
||||||
@import Dispatch;
|
@import Dispatch;
|
||||||
@import Foundation;
|
@import Foundation;
|
||||||
@import XCTest;
|
@import XCTest;
|
||||||
@import SocketIO;
|
@import SocketIO;
|
||||||
|
|
||||||
@interface SocketObjectiveCTest : XCTestCase
|
// TODO Manager interface tests
|
||||||
|
|
||||||
@property SocketIOClient* socket;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation SocketObjectiveCTest
|
@implementation SocketObjectiveCTest
|
||||||
|
|
||||||
- (void)setUp {
|
|
||||||
[super setUp];
|
|
||||||
NSURL* url = [[NSURL alloc] initWithString:@"http://localhost"];
|
|
||||||
self.socket = [[SocketIOClient alloc] initWithSocketURL:url config:@{@"log": @NO, @"forcePolling": @YES}];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testProperties {
|
- (void)testProperties {
|
||||||
NSURL* url = nil;
|
|
||||||
|
|
||||||
url = self.socket.socketURL;
|
|
||||||
self.socket.forceNew = false;
|
|
||||||
self.socket.handleQueue = dispatch_get_main_queue();
|
|
||||||
self.socket.nsp = @"/objective-c";
|
self.socket.nsp = @"/objective-c";
|
||||||
self.socket.reconnects = false;
|
if (self.socket.status == SocketIOStatusConnected) { }
|
||||||
self.socket.reconnectWait = 1;
|
|
||||||
if (self.socket.status == SocketIOClientStatusConnected) { }
|
|
||||||
if (self.socket.engine == NULL) { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testOnSyntax {
|
- (void)testOnSyntax {
|
||||||
@ -62,7 +46,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testJoinNamespaceSyntax {
|
- (void)testJoinNamespaceSyntax {
|
||||||
[self.socket joinNamespace:@"/objective-c"];
|
[self.socket joinNamespace];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testOnAnySyntax {
|
- (void)testOnAnySyntax {
|
||||||
@ -74,10 +58,6 @@
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testReconnectSyntax {
|
|
||||||
[self.socket reconnect];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testRemoveAllHandlersSyntax {
|
- (void)testRemoveAllHandlersSyntax {
|
||||||
[self.socket removeAllHandlers];
|
[self.socket removeAllHandlers];
|
||||||
}
|
}
|
||||||
@ -94,15 +74,16 @@
|
|||||||
[self.socket off:@"test"];
|
[self.socket off:@"test"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testSocketManager {
|
|
||||||
SocketClientManager* manager = [SocketClientManager sharedManager];
|
|
||||||
[manager addSocket:self.socket labeledAs:@"test"];
|
|
||||||
[manager removeSocketWithLabel:@"test"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testSSLSecurity {
|
- (void)testSSLSecurity {
|
||||||
SSLSecurity* sec = [[SSLSecurity alloc] initWithUsePublicKeys:0];
|
SSLSecurity* sec = [[SSLSecurity alloc] initWithUsePublicKeys:0];
|
||||||
sec = nil;
|
sec = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setUp {
|
||||||
|
[super setUp];
|
||||||
|
NSURL* url = [[NSURL alloc] initWithString:@"http://localhost"];
|
||||||
|
self.manager = [[SocketManager alloc] initWithSocketURL:url config:nil];
|
||||||
|
self.socket = [self.manager defaultSocket];
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@ -13,11 +13,11 @@ One of the most common reasons your event might not be called is if the client i
|
|||||||
Take this code for example:
|
Take this code for example:
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
class SocketManager {
|
class Manager {
|
||||||
func addHandlers() {
|
func addHandlers() {
|
||||||
let socket = SocketIOClient(socketURL: URL(string: "http://somesocketioserver.com")!)
|
let manager = SocketManager(socketURL: URL(string: "http://somesocketioserver.com")!)
|
||||||
|
|
||||||
socket.on("myEvent") {data, ack in
|
manager.defaultSocket.on("myEvent") {data, ack in
|
||||||
print(data)
|
print(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -25,30 +25,20 @@ class SocketManager {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This code is **incorrect**, and the event handler will never be called. Because as soon as this method is called `socket`
|
This code is **incorrect**, and the event handler will never be called. Because as soon as this method is called `manager`
|
||||||
will be released and its memory reclaimed.
|
will be released, along with the socket, and its memory reclaimed.
|
||||||
|
|
||||||
A correct way would be:
|
A correct way would be:
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
class SocketManager {
|
class Manager {
|
||||||
let socket = SocketIOClient(socketURL: URL(string: "http://somesocketioserver.com")!)
|
let manager = SocketManager(socketURL: URL(string: "http://somesocketioserver.com")!)
|
||||||
|
|
||||||
func addHandlers() {
|
func addHandlers() {
|
||||||
socket.on("myEvent") {data, ack in
|
manager.defaultSocket.on("myEvent") {data, ack in
|
||||||
print(data)
|
print(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
------
|
|
||||||
|
|
||||||
Another case where this might happen is if you use namespaces in your socket.io application.
|
|
||||||
|
|
||||||
In the JavaScript client a url that looks like `http://somesocketioserver.com/client` would be done with the `nsp` config.
|
|
||||||
|
|
||||||
```swift
|
|
||||||
let socket = SocketIOClient(socketURL: URL(string: "http://somesocketioserver.com")!, config: [.nsp("/client")])
|
|
||||||
```
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user