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
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										20
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								README.md
									
									
									
									
									
								
							@ -7,20 +7,21 @@ Socket.IO-client for iOS/OS X.
 | 
			
		||||
```swift
 | 
			
		||||
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
 | 
			
		||||
    print("socket connected")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
socket.on("currentAmount") {data, ack in
 | 
			
		||||
    if let cur = data[0] as? Double {
 | 
			
		||||
        socket.emitWithAck("canUpdate", cur).timingOut(after: 0) {data in
 | 
			
		||||
            socket.emit("update", ["amount": cur + 2.50])
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ack.with("Got your currentAmount", "dude")
 | 
			
		||||
    guard let cur = data[0] as? Double else { return }
 | 
			
		||||
    
 | 
			
		||||
    socket.emitWithAck("canUpdate", cur).timingOut(after: 0) {data in
 | 
			
		||||
        socket.emit("update", ["amount": cur + 2.50])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ack.with("Got your currentAmount", "dude")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
socket.connect()
 | 
			
		||||
@ -29,8 +30,10 @@ socket.connect()
 | 
			
		||||
## Objective-C Example
 | 
			
		||||
```objective-c
 | 
			
		||||
@import SocketIO;
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
    NSLog(@"socket connected");
 | 
			
		||||
@ -134,6 +137,7 @@ Objective-C:
 | 
			
		||||
# [Docs](https://nuclearace.github.io/Socket.IO-Client-Swift/index.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)
 | 
			
		||||
- [Options](https://nuclearace.github.io/Socket.IO-Client-Swift/Enums/SocketIOClientOption.html)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,8 @@
 | 
			
		||||
	objects = {
 | 
			
		||||
 | 
			
		||||
/* 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 */; };
 | 
			
		||||
		1C686BE31F869AFD007D8627 /* SocketEngineTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C686BD31F869AF1007D8627 /* SocketEngineTest.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 */; };
 | 
			
		||||
		DD52B44AE56F2E07F3F3F991 /* SocketAckManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B09F7984E730513AB7E5 /* SocketAckManager.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 */; };
 | 
			
		||||
		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 */; };
 | 
			
		||||
		DD52B9412F660F828B683422 /* SocketParsable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B31D0E6815F5F10CEFB6 /* SocketParsable.swift */; };
 | 
			
		||||
		DD52BB69B6D260035B652CA4 /* SocketAnyEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B5A9DE10C7A8AD35617F /* SocketAnyEvent.swift */; };
 | 
			
		||||
		DD52BB82239886CF6ADD642C /* SocketEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B7A9779A2E08075E5AAC /* SocketEngine.swift */; };
 | 
			
		||||
		DD52BB9A3E42FF2DD6BE7C2F /* SocketIOClientSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52BCAF915A546288664346 /* SocketIOClientSpec.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 */; };
 | 
			
		||||
		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 */; };
 | 
			
		||||
		DD52BFBC9E7CC32D3515AC80 /* SocketEngineSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B645273A873667BC2D43 /* SocketEngineSpec.swift */; };
 | 
			
		||||
		DD52BFEB4DBD3BF8D93DAEFF /* SocketEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B6DCCBBAC6BE9C22568D /* SocketEventHandler.swift */; };
 | 
			
		||||
@ -57,6 +60,8 @@
 | 
			
		||||
/* End PBXContainerItemProxy 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>"; };
 | 
			
		||||
		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>"; };
 | 
			
		||||
@ -83,8 +88,8 @@
 | 
			
		||||
		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>"; };
 | 
			
		||||
		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>"; };
 | 
			
		||||
		DD52B282975446C9A9C56D7B /* SocketClientManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketClientManager.swift; sourceTree = "<group>"; };
 | 
			
		||||
		DD52B1D9BC4AE46D38D827DE /* SocketIOStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketIOStatus.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>"; };
 | 
			
		||||
		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>"; };
 | 
			
		||||
@ -95,8 +100,11 @@
 | 
			
		||||
		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>"; };
 | 
			
		||||
		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>"; };
 | 
			
		||||
		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>"; };
 | 
			
		||||
		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>"; };
 | 
			
		||||
@ -128,6 +136,16 @@
 | 
			
		||||
/* End PBXFrameworksBuildPhase 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 */ = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
@ -138,6 +156,7 @@
 | 
			
		||||
				1C686BD61F869AF1007D8627 /* SocketAckManagerTest.swift */,
 | 
			
		||||
				1C686BD71F869AF1007D8627 /* SocketParserTest.swift */,
 | 
			
		||||
				1C686BD81F869AF1007D8627 /* SocketNamespacePacketTest.swift */,
 | 
			
		||||
				DD52BBAC5FAA7730D32CD5BF /* SocketMangerTest.swift */,
 | 
			
		||||
			);
 | 
			
		||||
			name = TestSocketIO;
 | 
			
		||||
			path = Tests/TestSocketIO;
 | 
			
		||||
@ -147,6 +166,9 @@
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
				1C686BFE1F869E9D007D8627 /* SocketObjectiveCTest.m */,
 | 
			
		||||
				DD52BB5E907D283ACC31E17F /* ManagerObjectiveCTest.m */,
 | 
			
		||||
				DD52B8396C7DEE7BFD6A985A /* ManagerObjectiveCTest.h */,
 | 
			
		||||
				DD52B2C54A6ADF3371C13DCB /* SocketObjectiveCTest.h */,
 | 
			
		||||
			);
 | 
			
		||||
			name = TestSocketIOObjc;
 | 
			
		||||
			path = Tests/TestSocketIOObjc;
 | 
			
		||||
@ -198,6 +220,7 @@
 | 
			
		||||
				DD52B6A0966AF71393777311 /* Client */,
 | 
			
		||||
				DD52B1D10D761CEF3944A6BC /* Util */,
 | 
			
		||||
				DD52B647ED881F3FF6EEC617 /* Parse */,
 | 
			
		||||
				1C657951DEA2E0293D0FD1B6 /* Manager */,
 | 
			
		||||
			);
 | 
			
		||||
			name = Source;
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
@ -251,7 +274,6 @@
 | 
			
		||||
				DD52BED81BF312B0E90E92AC /* SocketLogger.swift */,
 | 
			
		||||
				DD52B471D780013E18DF9335 /* SocketExtensions.swift */,
 | 
			
		||||
				DD52BA240D139F72633D4159 /* SocketStringReader.swift */,
 | 
			
		||||
				DD52B282975446C9A9C56D7B /* SocketClientManager.swift */,
 | 
			
		||||
				9432E0061F77F7CA006AF628 /* SSLSecurity.swift */,
 | 
			
		||||
			);
 | 
			
		||||
			name = Util;
 | 
			
		||||
@ -276,7 +298,7 @@
 | 
			
		||||
				DD52B6DCCBBAC6BE9C22568D /* SocketEventHandler.swift */,
 | 
			
		||||
				DD52BCAF915A546288664346 /* SocketIOClientSpec.swift */,
 | 
			
		||||
				DD52B078DB0A3C3D1BB507CD /* SocketIOClientOption.swift */,
 | 
			
		||||
				DD52B1D9BC4AE46D38D827DE /* SocketIOClientStatus.swift */,
 | 
			
		||||
				DD52B1D9BC4AE46D38D827DE /* SocketIOStatus.swift */,
 | 
			
		||||
				DD52B57FFEE8560CFFD793B3 /* SocketIOClientConfiguration.swift */,
 | 
			
		||||
			);
 | 
			
		||||
			name = Client;
 | 
			
		||||
@ -448,15 +470,16 @@
 | 
			
		||||
				9432E00F1F77F8C4006AF628 /* SSLSecurity.swift in Sources */,
 | 
			
		||||
				DD52BB9A3E42FF2DD6BE7C2F /* SocketIOClientSpec.swift in Sources */,
 | 
			
		||||
				DD52B2AFE7D46039C7AE4D19 /* SocketIOClientOption.swift in Sources */,
 | 
			
		||||
				DD52BE4D1E6BB752CD9614A6 /* SocketIOClientStatus.swift in Sources */,
 | 
			
		||||
				DD52BE4D1E6BB752CD9614A6 /* SocketIOStatus.swift in Sources */,
 | 
			
		||||
				DD52BD065B74AC5B77BAEFAA /* SocketIOClientConfiguration.swift in Sources */,
 | 
			
		||||
				DD52B048C71D724ABBD18C71 /* SocketTypes.swift in Sources */,
 | 
			
		||||
				DD52BC3F1F880820E8FDFD0C /* SocketLogger.swift in Sources */,
 | 
			
		||||
				DD52B56DE03CDB4F40BD1A23 /* SocketExtensions.swift in Sources */,
 | 
			
		||||
				DD52B11AF936352BAE30B2C8 /* SocketStringReader.swift in Sources */,
 | 
			
		||||
				DD52B660D63B6A25C3755AA7 /* SocketClientManager.swift in Sources */,
 | 
			
		||||
				DD52B57E7ABC61B57EE2A4B8 /* SocketPacket.swift in Sources */,
 | 
			
		||||
				DD52B9412F660F828B683422 /* SocketParsable.swift in Sources */,
 | 
			
		||||
				1C6572803D7E252A77A86E5F /* SocketManager.swift in Sources */,
 | 
			
		||||
				1C657FBB3F670261780FD72E /* SocketManagerSpec.swift in Sources */,
 | 
			
		||||
			);
 | 
			
		||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
			
		||||
		};
 | 
			
		||||
@ -472,6 +495,8 @@
 | 
			
		||||
				1C686BE61F869AFD007D8627 /* SocketAckManagerTest.swift in Sources */,
 | 
			
		||||
				1C686BE71F869AFD007D8627 /* SocketParserTest.swift in Sources */,
 | 
			
		||||
				1C686BE81F869AFD007D8627 /* SocketNamespacePacketTest.swift in Sources */,
 | 
			
		||||
				DD52BCCD25EFA76E0F9B313C /* SocketMangerTest.swift in Sources */,
 | 
			
		||||
				DD52B53F2609D91A683DFCDD /* ManagerObjectiveCTest.m in Sources */,
 | 
			
		||||
			);
 | 
			
		||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
@ -120,8 +120,10 @@ public final class OnAckCallback : NSObject {
 | 
			
		||||
 | 
			
		||||
        guard seconds != 0 else { return }
 | 
			
		||||
 | 
			
		||||
        socket.handleQueue.asyncAfter(deadline: DispatchTime.now() + seconds) {
 | 
			
		||||
            socket.ackHandlers.timeoutAck(self.ackNumber, onQueue: socket.handleQueue)
 | 
			
		||||
        socket.manager?.handleQueue.asyncAfter(deadline: DispatchTime.now() + seconds) {[weak socket] in
 | 
			
		||||
            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 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.
 | 
			
		||||
open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, SocketParsable {
 | 
			
		||||
/// For example:
 | 
			
		||||
///
 | 
			
		||||
/// ```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
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
    ///
 | 
			
		||||
    /// **Must** start with a `/`.
 | 
			
		||||
    @objc
 | 
			
		||||
    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.
 | 
			
		||||
    @objc
 | 
			
		||||
    public var sid: String? {
 | 
			
		||||
        return engine?.sid
 | 
			
		||||
    public var sid: String {
 | 
			
		||||
        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.
 | 
			
		||||
    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.
 | 
			
		||||
    public private(set) var handlers = [SocketEventHandler]()
 | 
			
		||||
 | 
			
		||||
    /// The manager for this socket.
 | 
			
		||||
    @objc
 | 
			
		||||
    public private(set) weak var manager: SocketManagerSpec?
 | 
			
		||||
 | 
			
		||||
    /// The status of this client.
 | 
			
		||||
    @objc
 | 
			
		||||
    public private(set) var status = SocketIOClientStatus.notConnected {
 | 
			
		||||
    public private(set) var status = SocketIOStatus.notConnected {
 | 
			
		||||
        didSet {
 | 
			
		||||
            switch status {
 | 
			
		||||
            case .connected:
 | 
			
		||||
                reconnecting = false
 | 
			
		||||
                currentReconnectAttempt = 0
 | 
			
		||||
            default:
 | 
			
		||||
                break
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            handleClientEvent(.statusChange, data: [status])
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -136,60 +78,29 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
 | 
			
		||||
    var ackHandlers = SocketAckManager()
 | 
			
		||||
 | 
			
		||||
    private(set) var currentAck = -1
 | 
			
		||||
    private(set) var reconnectAttempts = -1
 | 
			
		||||
 | 
			
		||||
    private var _config: SocketIOClientConfiguration
 | 
			
		||||
    private var currentReconnectAttempt = 0
 | 
			
		||||
    private var reconnecting = false
 | 
			
		||||
    private lazy var logType = "SocketIOClient{\(nsp)}"
 | 
			
		||||
 | 
			
		||||
    // MARK: Initializers
 | 
			
		||||
 | 
			
		||||
    /// 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 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)
 | 
			
		||||
    @objc
 | 
			
		||||
    public init(manager: SocketManagerSpec, nsp: String) {
 | 
			
		||||
        self.manager = manager
 | 
			
		||||
        self.nsp = nsp
 | 
			
		||||
 | 
			
		||||
        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 {
 | 
			
		||||
        DefaultSocketLogger.Logger.log("Client is being released", type: SocketIOClient.logType)
 | 
			
		||||
        engine?.disconnect(reason: "Client Deinit")
 | 
			
		||||
        DefaultSocketLogger.Logger.log("Client is being released", type: logType)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 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.
 | 
			
		||||
    ///
 | 
			
		||||
    /// 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: (() -> ())?) {
 | 
			
		||||
        assert(timeoutAfter >= 0, "Invalid timeout: \(timeoutAfter)")
 | 
			
		||||
 | 
			
		||||
        guard status != .connected else {
 | 
			
		||||
            DefaultSocketLogger.Logger.log("Tried connecting on an already connected socket",
 | 
			
		||||
                                           type: SocketIOClient.logType)
 | 
			
		||||
        guard let manager = self.manager, status != .connected else {
 | 
			
		||||
            DefaultSocketLogger.Logger.log("Tried connecting on an already connected socket", type: logType)
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        status = .connecting
 | 
			
		||||
 | 
			
		||||
        if engine == nil || forceNew {
 | 
			
		||||
            addEngine()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        engine?.connect()
 | 
			
		||||
        manager.connectSocket(self)
 | 
			
		||||
 | 
			
		||||
        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 }
 | 
			
		||||
 | 
			
		||||
            this.status = .disconnected
 | 
			
		||||
            this.engine?.disconnect(reason: "Connect timeout")
 | 
			
		||||
            this.leaveNamespace()
 | 
			
		||||
 | 
			
		||||
            handler?()
 | 
			
		||||
        }
 | 
			
		||||
@ -248,7 +154,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
 | 
			
		||||
    open func didConnect(toNamespace namespace: String) {
 | 
			
		||||
        guard status != .connected else { return }
 | 
			
		||||
 | 
			
		||||
        DefaultSocketLogger.Logger.log("Socket connected", type: SocketIOClient.logType)
 | 
			
		||||
        DefaultSocketLogger.Logger.log("Socket connected", type: logType)
 | 
			
		||||
 | 
			
		||||
        status = .connected
 | 
			
		||||
 | 
			
		||||
@ -261,21 +167,22 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
 | 
			
		||||
    open func didDisconnect(reason: String) {
 | 
			
		||||
        guard status != .disconnected else { return }
 | 
			
		||||
 | 
			
		||||
        DefaultSocketLogger.Logger.log("Disconnected: \(reason)", type: SocketIOClient.logType)
 | 
			
		||||
        DefaultSocketLogger.Logger.log("Disconnected: \(reason)", type: logType)
 | 
			
		||||
 | 
			
		||||
        reconnecting = false
 | 
			
		||||
        status = .disconnected
 | 
			
		||||
 | 
			
		||||
        // Make sure the engine is actually dead.
 | 
			
		||||
        engine?.disconnect(reason: reason)
 | 
			
		||||
        handleClientEvent(.disconnect, data: [reason])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// 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
 | 
			
		||||
    open func disconnect() {
 | 
			
		||||
        DefaultSocketLogger.Logger.log("Closing socket", type: SocketIOClient.logType)
 | 
			
		||||
        DefaultSocketLogger.Logger.log("Closing socket", type: logType)
 | 
			
		||||
 | 
			
		||||
        leaveNamespace()
 | 
			
		||||
        didDisconnect(reason: "Disconnect")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -291,7 +198,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
 | 
			
		||||
            try emit(event, with: items.map({ try $0.socketRepresentation() }))
 | 
			
		||||
        } catch let err {
 | 
			
		||||
            DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)",
 | 
			
		||||
                                             type: SocketIOClient.logType)
 | 
			
		||||
                                             type: logType)
 | 
			
		||||
 | 
			
		||||
            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() }))
 | 
			
		||||
        } catch let err {
 | 
			
		||||
            DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)",
 | 
			
		||||
                                             type: SocketIOClient.logType)
 | 
			
		||||
                                             type: logType)
 | 
			
		||||
 | 
			
		||||
            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 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`.
 | 
			
		||||
@ -390,91 +297,9 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
 | 
			
		||||
        let packet = SocketPacket.packetFromEmit(items, id: ack, nsp: nsp, ack: true)
 | 
			
		||||
        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)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// 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: [])
 | 
			
		||||
        manager?.engine?.send(str, withData: packet.binary)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// 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.
 | 
			
		||||
    @objc
 | 
			
		||||
    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.
 | 
			
		||||
@ -500,7 +333,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
 | 
			
		||||
    open func handleEvent(_ event: String, data: [Any], isInternalMessage: Bool, withAck ack: Int = -1) {
 | 
			
		||||
        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))
 | 
			
		||||
 | 
			
		||||
@ -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 data: The data for this event.
 | 
			
		||||
    open func handleClientEvent(_ event: SocketClientEvent, data: [Any]) {
 | 
			
		||||
        handleEvent(event.rawValue, data: data, isInternalMessage: true)
 | 
			
		||||
    /// - parameter pack: The packet to handle.
 | 
			
		||||
    open func handlePacket(_ pack: SocketPacket) {
 | 
			
		||||
        func handleConnect(_ packetNamespace: String) {
 | 
			
		||||
            guard packetNamespace == nsp else { return }
 | 
			
		||||
 | 
			
		||||
            didConnect(toNamespace: packetNamespace)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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 return to the default namespace.
 | 
			
		||||
    /// Call when you wish to leave a namespace and disconnect this socket.
 | 
			
		||||
    @objc
 | 
			
		||||
    open func leaveNamespace() {
 | 
			
		||||
        guard nsp != "/" else { return }
 | 
			
		||||
 | 
			
		||||
        engine?.send("1\(nsp)", withData: [])
 | 
			
		||||
        nsp = "/"
 | 
			
		||||
        status = .disconnected
 | 
			
		||||
 | 
			
		||||
        manager?.disconnectSocket(self)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Joins `namespace`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// **Do not use this to join the default namespace.** Instead call `leaveNamespace`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// - parameter namespace: The namespace to join.
 | 
			
		||||
    /// Joins `nsp`.
 | 
			
		||||
    @objc
 | 
			
		||||
    open func joinNamespace(_ namespace: String) {
 | 
			
		||||
        guard namespace != "/" else { return }
 | 
			
		||||
    open func joinNamespace() {
 | 
			
		||||
        guard nsp != "/" else { return }
 | 
			
		||||
 | 
			
		||||
        DefaultSocketLogger.Logger.log("Joining namespace \(namespace)", type: SocketIOClient.logType)
 | 
			
		||||
        DefaultSocketLogger.Logger.log("Joining namespace \(nsp)", type: logType)
 | 
			
		||||
 | 
			
		||||
        nsp = namespace
 | 
			
		||||
        engine?.send("0\(nsp)", withData: [])
 | 
			
		||||
        manager?.engine?.send("0\(nsp)", withData: [])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// 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.
 | 
			
		||||
    @objc
 | 
			
		||||
    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 })
 | 
			
		||||
    }
 | 
			
		||||
@ -569,7 +415,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
 | 
			
		||||
    /// - parameter id: The UUID of the handler you wish to remove.
 | 
			
		||||
    @objc
 | 
			
		||||
    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 })
 | 
			
		||||
    }
 | 
			
		||||
@ -582,7 +428,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
 | 
			
		||||
    @objc
 | 
			
		||||
    @discardableResult
 | 
			
		||||
    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)
 | 
			
		||||
        handlers.append(handler)
 | 
			
		||||
@ -626,7 +472,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
 | 
			
		||||
    @objc
 | 
			
		||||
    @discardableResult
 | 
			
		||||
    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()
 | 
			
		||||
 | 
			
		||||
@ -649,31 +495,10 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
 | 
			
		||||
        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.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This will cause a `disconnect` event to be emitted, as well as an `reconnectAttempt` event.
 | 
			
		||||
    @objc
 | 
			
		||||
    open func reconnect() {
 | 
			
		||||
        guard !reconnecting else { return }
 | 
			
		||||
 | 
			
		||||
        engine?.disconnect(reason: "manual reconnect")
 | 
			
		||||
    }
 | 
			
		||||
    @available(*, unavailable, message: "Call the manager's reconnect method")
 | 
			
		||||
    open func reconnect() { }
 | 
			
		||||
 | 
			
		||||
    /// Removes all handlers.
 | 
			
		||||
    ///
 | 
			
		||||
@ -683,54 +508,15 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
 | 
			
		||||
        handlers.removeAll(keepingCapacity: false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private func tryReconnect(reason: String) {
 | 
			
		||||
        guard reconnecting else { return }
 | 
			
		||||
    /// 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 reconnecting.
 | 
			
		||||
    @objc
 | 
			
		||||
    open func setReconnecting(reason: String) {
 | 
			
		||||
        status = .connecting
 | 
			
		||||
 | 
			
		||||
        DefaultSocketLogger.Logger.log("Starting reconnect", type: SocketIOClient.logType)
 | 
			
		||||
        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
 | 
			
		||||
@ -743,14 +529,10 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
 | 
			
		||||
        status = .connected
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    func setTestStatus(_ status: SocketIOClientStatus) {
 | 
			
		||||
    func setTestStatus(_ status: SocketIOStatus) {
 | 
			
		||||
        self.status = status
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    func setTestEngine(_ engine: SocketEngineSpec?) {
 | 
			
		||||
        self.engine = engine
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    func emitTest(event: String, _ data: Any...) {
 | 
			
		||||
        emit([event] + data)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -125,5 +125,12 @@ public struct SocketIOClientConfiguration : ExpressibleByArrayLiteral, Collectio
 | 
			
		||||
 | 
			
		||||
        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.
 | 
			
		||||
    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.
 | 
			
		||||
    case extraHeaders([String: String])
 | 
			
		||||
 | 
			
		||||
@ -67,10 +63,6 @@ public enum SocketIOClientOption : ClientOption {
 | 
			
		||||
    /// Used to pass in a custom logger.
 | 
			
		||||
    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.
 | 
			
		||||
    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.
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
    /// The description of this option.
 | 
			
		||||
@ -114,8 +101,6 @@ public enum SocketIOClientOption : ClientOption {
 | 
			
		||||
            description = "connectParams"
 | 
			
		||||
        case .cookies:
 | 
			
		||||
            description = "cookies"
 | 
			
		||||
        case .doubleEncodeUTF8:
 | 
			
		||||
            description = "doubleEncodeUTF8"
 | 
			
		||||
        case .extraHeaders:
 | 
			
		||||
            description = "extraHeaders"
 | 
			
		||||
        case .forceNew:
 | 
			
		||||
@ -130,8 +115,6 @@ public enum SocketIOClientOption : ClientOption {
 | 
			
		||||
            description = "log"
 | 
			
		||||
        case .logger:
 | 
			
		||||
            description = "logger"
 | 
			
		||||
        case .nsp:
 | 
			
		||||
            description = "nsp"
 | 
			
		||||
        case .path:
 | 
			
		||||
            description = "path"
 | 
			
		||||
        case .reconnects:
 | 
			
		||||
@ -148,8 +131,6 @@ public enum SocketIOClientOption : ClientOption {
 | 
			
		||||
            description = "security"
 | 
			
		||||
        case .sessionDelegate:
 | 
			
		||||
            description = "sessionDelegate"
 | 
			
		||||
        case .voipEnabled:
 | 
			
		||||
            description = "voipEnabled"
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return description
 | 
			
		||||
@ -165,8 +146,6 @@ public enum SocketIOClientOption : ClientOption {
 | 
			
		||||
            value = params
 | 
			
		||||
        case let .cookies(cookies):
 | 
			
		||||
            value = cookies
 | 
			
		||||
        case let .doubleEncodeUTF8(encode):
 | 
			
		||||
            value = encode
 | 
			
		||||
        case let .extraHeaders(headers):
 | 
			
		||||
            value = headers
 | 
			
		||||
        case let .forceNew(force):
 | 
			
		||||
@ -181,8 +160,6 @@ public enum SocketIOClientOption : ClientOption {
 | 
			
		||||
            value = log
 | 
			
		||||
        case let .logger(logger):
 | 
			
		||||
            value = logger
 | 
			
		||||
        case let .nsp(nsp):
 | 
			
		||||
            value = nsp
 | 
			
		||||
        case let .path(path):
 | 
			
		||||
            value = path
 | 
			
		||||
        case let .reconnects(reconnects):
 | 
			
		||||
@ -199,8 +176,6 @@ public enum SocketIOClientOption : ClientOption {
 | 
			
		||||
            value = signed
 | 
			
		||||
        case let .sessionDelegate(delegate):
 | 
			
		||||
            value = delegate
 | 
			
		||||
        case let .voipEnabled(enabled):
 | 
			
		||||
            value = enabled
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return value
 | 
			
		||||
 | 
			
		||||
@ -32,22 +32,19 @@ public protocol SocketIOClientSpec : class {
 | 
			
		||||
    /// A handler that will be called on any event.
 | 
			
		||||
    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.
 | 
			
		||||
    var handlers: [SocketEventHandler] { get }
 | 
			
		||||
 | 
			
		||||
    /// The manager for this socket.
 | 
			
		||||
    var manager: SocketManagerSpec? { get }
 | 
			
		||||
 | 
			
		||||
    /// The namespace that this socket is currently connected to.
 | 
			
		||||
    ///
 | 
			
		||||
    /// **Must** start with a `/`.
 | 
			
		||||
    var nsp: String { get set }
 | 
			
		||||
 | 
			
		||||
    /// The status of this client.
 | 
			
		||||
    var status: SocketIOClientStatus { get }
 | 
			
		||||
    var status: SocketIOStatus { get }
 | 
			
		||||
 | 
			
		||||
    // MARK: Methods
 | 
			
		||||
 | 
			
		||||
@ -126,6 +123,12 @@ public protocol SocketIOClientSpec : class {
 | 
			
		||||
    /// - parameter data: The data sent back with this ack.
 | 
			
		||||
    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.
 | 
			
		||||
    ///
 | 
			
		||||
    /// - 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.
 | 
			
		||||
    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 data: The data for this event.
 | 
			
		||||
    func handleClientEvent(_ event: SocketClientEvent, data: [Any])
 | 
			
		||||
    /// - parameter pack: The packet to handle.
 | 
			
		||||
    func handlePacket(_ pack: SocketPacket)
 | 
			
		||||
 | 
			
		||||
    /// 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()
 | 
			
		||||
 | 
			
		||||
    /// Joins `namespace`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// **Do not use this to join the default namespace.** Instead call `leaveNamespace`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// - parameter namespace: The namespace to join.
 | 
			
		||||
    func joinNamespace(_ namespace: String)
 | 
			
		||||
    /// Joins `nsp`.
 | 
			
		||||
    func joinNamespace()
 | 
			
		||||
 | 
			
		||||
    /// 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.
 | 
			
		||||
    func onAny(_ handler: @escaping (SocketAnyEvent) -> ())
 | 
			
		||||
 | 
			
		||||
    /// Tries to reconnect to the server.
 | 
			
		||||
    func reconnect()
 | 
			
		||||
 | 
			
		||||
    /// Removes all handlers.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Can be used after disconnecting to break any potential remaining retain cycles.
 | 
			
		||||
    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 {
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
//
 | 
			
		||||
//  SocketIOClientStatus.swift
 | 
			
		||||
//  SocketIOStatus.swift
 | 
			
		||||
//  Socket.IO-Client-Swift
 | 
			
		||||
//
 | 
			
		||||
//  Created by Erik Little on 8/14/15.
 | 
			
		||||
@ -24,17 +24,36 @@
 | 
			
		||||
 | 
			
		||||
import Foundation
 | 
			
		||||
 | 
			
		||||
/// Represents the state of the client.
 | 
			
		||||
@objc public enum SocketIOClientStatus : Int {
 | 
			
		||||
    /// The client has never been connected. Or the client has been reset.
 | 
			
		||||
/// Represents state of a manager or client.
 | 
			
		||||
@objc
 | 
			
		||||
public enum SocketIOStatus : Int, CustomStringConvertible {
 | 
			
		||||
    // MARK: Cases
 | 
			
		||||
 | 
			
		||||
    /// The client/manager has never been connected. Or the client has been reset.
 | 
			
		||||
    case notConnected
 | 
			
		||||
 | 
			
		||||
    /// The client was once connected, but not anymore.
 | 
			
		||||
    /// The client/manager was once connected, but not anymore.
 | 
			
		||||
    case disconnected
 | 
			
		||||
 | 
			
		||||
    /// The client is in the process of connecting.
 | 
			
		||||
    /// The client/manager is in the process of connecting.
 | 
			
		||||
    case connecting
 | 
			
		||||
 | 
			
		||||
    /// The client is currently connected.
 | 
			
		||||
    /// The client/manager is currently 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.
 | 
			
		||||
/// 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
 | 
			
		||||
 | 
			
		||||
    private static let logType = "SocketEngine"
 | 
			
		||||
@ -147,41 +148,11 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
 | 
			
		||||
    public init(client: SocketEngineClient, url: URL, config: SocketIOClientConfiguration) {
 | 
			
		||||
        self.client = client
 | 
			
		||||
        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()
 | 
			
		||||
 | 
			
		||||
        setConfigs(config)
 | 
			
		||||
 | 
			
		||||
        sessionDelegate = sessionDelegate ?? self
 | 
			
		||||
 | 
			
		||||
        (urlPolling, urlWebSocket) = createURLs()
 | 
			
		||||
@ -572,6 +543,44 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
 | 
			
		||||
        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
 | 
			
		||||
    private func upgradeTransport() {
 | 
			
		||||
        if ws?.isConnected ?? false {
 | 
			
		||||
@ -645,6 +654,12 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
 | 
			
		||||
            client?.engineDidClose(reason: "Socket Disconnected")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Test Properties
 | 
			
		||||
 | 
			
		||||
    func setConnected(_ value: Bool) {
 | 
			
		||||
        connected = value
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extension SocketEngine {
 | 
			
		||||
 | 
			
		||||
@ -34,6 +34,9 @@ import Starscream
 | 
			
		||||
    /// `true` if this engine is closed.
 | 
			
		||||
    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.
 | 
			
		||||
    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 {
 | 
			
		||||
                return binary[dict["num"] as! Int]
 | 
			
		||||
            } else {
 | 
			
		||||
                return dict.reduce(JSON(), {cur, keyValue in
 | 
			
		||||
                    var cur = cur
 | 
			
		||||
 | 
			
		||||
                return dict.reduce(into: JSON(), {cur, keyValue in
 | 
			
		||||
                    cur[keyValue.0] = _fillInPlaceholders(keyValue.1)
 | 
			
		||||
 | 
			
		||||
                    return cur
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
        case let arr as [Any]:
 | 
			
		||||
@ -225,12 +221,8 @@ private extension SocketPacket {
 | 
			
		||||
        case let arr as [Any]:
 | 
			
		||||
            return arr.map({shred($0, binary: &binary)})
 | 
			
		||||
        case let dict as JSON:
 | 
			
		||||
            return dict.reduce(JSON(), {cur, keyValue in
 | 
			
		||||
                var mutCur = cur
 | 
			
		||||
 | 
			
		||||
                mutCur[keyValue.0] = shred(keyValue.1, binary: &binary)
 | 
			
		||||
 | 
			
		||||
                return mutCur
 | 
			
		||||
            return dict.reduce(into: JSON(), {cur, keyValue in
 | 
			
		||||
                cur[keyValue.0] = shred(keyValue.1, binary: &binary)
 | 
			
		||||
            })
 | 
			
		||||
        default:
 | 
			
		||||
            return data
 | 
			
		||||
 | 
			
		||||
@ -26,14 +26,6 @@ import Foundation
 | 
			
		||||
public protocol SocketParsable : class {
 | 
			
		||||
    // 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
 | 
			
		||||
 | 
			
		||||
    /// 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.
 | 
			
		||||
    ///
 | 
			
		||||
    /// - 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.
 | 
			
		||||
    ///
 | 
			
		||||
    /// - 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.
 | 
			
		||||
@ -65,38 +58,18 @@ public enum SocketParsableError : Error {
 | 
			
		||||
    case invalidPacketType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public extension SocketParsable where Self: SocketIOClientSpec {
 | 
			
		||||
    private func isCorrectNamespace(_ nsp: String) -> Bool {
 | 
			
		||||
        return nsp == self.nsp
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private func handleConnect(_ packetNamespace: String) {
 | 
			
		||||
        guard packetNamespace == nsp else { return }
 | 
			
		||||
 | 
			
		||||
        didConnect(toNamespace: packetNamespace)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
/// Says that a type will be able to buffer binary data before all data for an event has come in.
 | 
			
		||||
public protocol SocketDataBufferable : class {
 | 
			
		||||
    /// 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 }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public extension SocketParsable where Self: SocketManagerSpec & SocketDataBufferable {
 | 
			
		||||
    /// Parses a message from the engine, returning a complete SocketPacket or throwing.
 | 
			
		||||
    ///
 | 
			
		||||
    /// - 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.
 | 
			
		||||
    ///
 | 
			
		||||
    /// - parameter message: The string that needs parsing.
 | 
			
		||||
    public func parseSocketMessage(_ message: String) {
 | 
			
		||||
        guard !message.isEmpty else { return }
 | 
			
		||||
    /// - returns: A completed socket packet or nil if the packet is invalid.
 | 
			
		||||
    public func parseSocketMessage(_ message: String) -> SocketPacket? {
 | 
			
		||||
        guard !message.isEmpty else { return nil }
 | 
			
		||||
 | 
			
		||||
        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")
 | 
			
		||||
 | 
			
		||||
            handlePacket(packet)
 | 
			
		||||
            return packet
 | 
			
		||||
        } catch {
 | 
			
		||||
            DefaultSocketLogger.Logger.error("\(error): \(message)", type: "SocketParser")
 | 
			
		||||
 | 
			
		||||
            return nil
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -192,21 +168,17 @@ public extension SocketParsable where Self: SocketIOClientSpec {
 | 
			
		||||
    /// into the correct placeholder.
 | 
			
		||||
    ///
 | 
			
		||||
    /// - 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 {
 | 
			
		||||
            DefaultSocketLogger.Logger.error("Got data when not remaking packet", type: "SocketParser")
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
            return nil
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 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()
 | 
			
		||||
 | 
			
		||||
        if packet.type != .binaryAck {
 | 
			
		||||
            handleEvent(packet.event, data: packet.args, isInternalMessage: false, withAck: packet.id)
 | 
			
		||||
        } else {
 | 
			
		||||
            handleAck(packet.id, data: packet.args)
 | 
			
		||||
        }
 | 
			
		||||
        return waitingPackets.removeLast()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
        case let ("logger", logger as SocketLogger):
 | 
			
		||||
            return .logger(logger)
 | 
			
		||||
        case let ("nsp", nsp as String):
 | 
			
		||||
            return .nsp(nsp)
 | 
			
		||||
        case let ("path", path as String):
 | 
			
		||||
            return .path(path)
 | 
			
		||||
        case let ("reconnects", reconnects as Bool):
 | 
			
		||||
 | 
			
		||||
@ -150,10 +150,9 @@ class SocketBasicPacketTest: XCTestCase {
 | 
			
		||||
 | 
			
		||||
    func testBinaryStringPlaceholderInMessage() {
 | 
			
		||||
        let engineString = "52-[\"test\",\"~~0\",{\"num\":0,\"_placeholder\":true},{\"_placeholder\":true,\"num\":1}]"
 | 
			
		||||
        let socket = SocketIOClient(socketURL: URL(string: "http://localhost/")!)
 | 
			
		||||
        socket.setTestable()
 | 
			
		||||
        let manager = SocketManager(socketURL: URL(string: "http://localhost/")!)
 | 
			
		||||
 | 
			
		||||
        var packet = try! socket.parseString(engineString)
 | 
			
		||||
        var packet = try! manager.parseString(engineString)
 | 
			
		||||
 | 
			
		||||
        XCTAssertEqual(packet.event, "test")
 | 
			
		||||
        _ = packet.addData(data)
 | 
			
		||||
 | 
			
		||||
@ -10,20 +10,9 @@ import XCTest
 | 
			
		||||
@testable import SocketIO
 | 
			
		||||
 | 
			
		||||
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() {
 | 
			
		||||
        let expect = expectation(description: "Basic polling test")
 | 
			
		||||
        client.on("blankTest") {data, ack in
 | 
			
		||||
        socket.on("blankTest") {data, ack in
 | 
			
		||||
            expect.fulfill()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -35,11 +24,11 @@ class SocketEngineTest: XCTestCase {
 | 
			
		||||
        let finalExpectation = expectation(description: "Final packet in poll test")
 | 
			
		||||
        var gotBlank = false
 | 
			
		||||
 | 
			
		||||
        client.on("blankTest") {data, ack in
 | 
			
		||||
        socket.on("blankTest") {data, ack in
 | 
			
		||||
            gotBlank = true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        client.on("stringTest") {data, ack in
 | 
			
		||||
        socket.on("stringTest") {data, ack in
 | 
			
		||||
            if let str = data[0] as? String, gotBlank {
 | 
			
		||||
                if str == "hello" {
 | 
			
		||||
                    finalExpectation.fulfill()
 | 
			
		||||
@ -54,7 +43,7 @@ class SocketEngineTest: XCTestCase {
 | 
			
		||||
    func testEngineDoesErrorOnUnknownTransport() {
 | 
			
		||||
        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" {
 | 
			
		||||
                finalExpectation.fulfill()
 | 
			
		||||
            }
 | 
			
		||||
@ -67,7 +56,7 @@ class SocketEngineTest: XCTestCase {
 | 
			
		||||
    func testEngineDoesErrorOnUnknownMessage() {
 | 
			
		||||
        let finalExpectation = expectation(description: "Engine Errors")
 | 
			
		||||
 | 
			
		||||
        client.on("error") {data, ack in
 | 
			
		||||
        socket.on("error") {data, ack in
 | 
			
		||||
            finalExpectation.fulfill()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -78,7 +67,7 @@ class SocketEngineTest: XCTestCase {
 | 
			
		||||
    func testEngineDecodesUTF8Properly() {
 | 
			
		||||
        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")
 | 
			
		||||
            expect.fulfill()
 | 
			
		||||
        }
 | 
			
		||||
@ -110,7 +99,7 @@ class SocketEngineTest: XCTestCase {
 | 
			
		||||
        let b64String = "b4aGVsbG8NCg=="
 | 
			
		||||
        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) {
 | 
			
		||||
                XCTAssertEqual(string, "hello")
 | 
			
		||||
            }
 | 
			
		||||
@ -123,4 +112,97 @@ class SocketEngineTest: XCTestCase {
 | 
			
		||||
 | 
			
		||||
        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()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -9,20 +9,12 @@
 | 
			
		||||
import XCTest
 | 
			
		||||
import SocketIO
 | 
			
		||||
 | 
			
		||||
class TestSocketIOClientConfiguration: XCTestCase {
 | 
			
		||||
    var config = [] as SocketIOClientConfiguration
 | 
			
		||||
 | 
			
		||||
    override func setUp() {
 | 
			
		||||
        super.setUp()
 | 
			
		||||
        
 | 
			
		||||
        config = [.log(false), .forceNew(true)]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
class TestSocketIOClientConfiguration : XCTestCase {
 | 
			
		||||
    func testReplaceSameOption() {
 | 
			
		||||
        config.insert(.log(true))
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        XCTAssertEqual(config.count, 2)
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        switch config[0] {
 | 
			
		||||
        case let .log(log):
 | 
			
		||||
            XCTAssertTrue(log)
 | 
			
		||||
@ -30,12 +22,12 @@ class TestSocketIOClientConfiguration: XCTestCase {
 | 
			
		||||
            XCTFail()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    func testIgnoreIfExisting() {
 | 
			
		||||
        config.insert(.forceNew(false), replacing: false)
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        XCTAssertEqual(config.count, 2)
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        switch config[1] {
 | 
			
		||||
        case let .forceNew(new):
 | 
			
		||||
            XCTAssertTrue(new)
 | 
			
		||||
@ -43,4 +35,12 @@ class TestSocketIOClientConfiguration: XCTestCase {
 | 
			
		||||
            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
 | 
			
		||||
 | 
			
		||||
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() {
 | 
			
		||||
        let message = "1"
 | 
			
		||||
        validateParseResult(message)
 | 
			
		||||
@ -108,7 +88,7 @@ class SocketParserTest: XCTestCase {
 | 
			
		||||
    func testInvalidInput() {
 | 
			
		||||
        let message = "8"
 | 
			
		||||
        do {
 | 
			
		||||
            let _ = try testSocket.parseString(message)
 | 
			
		||||
            let _ = try testManager.parseString(message)
 | 
			
		||||
            XCTFail()
 | 
			
		||||
        } catch {
 | 
			
		||||
 | 
			
		||||
@ -125,7 +105,7 @@ class SocketParserTest: XCTestCase {
 | 
			
		||||
 | 
			
		||||
    func validateParseResult(_ message: String) {
 | 
			
		||||
        let validValues = SocketParserTest.packetTypes[message]!
 | 
			
		||||
        let packet = try! testSocket.parseString(message)
 | 
			
		||||
        let packet = try! testManager.parseString(message)
 | 
			
		||||
        let type = String(message.characters.prefix(1))
 | 
			
		||||
 | 
			
		||||
        XCTAssertEqual(packet.type, SocketPacket.PacketType(rawValue: Int(type) ?? -1)!)
 | 
			
		||||
@ -139,8 +119,29 @@ class SocketParserTest: XCTestCase {
 | 
			
		||||
        let keys = Array(SocketParserTest.packetTypes.keys)
 | 
			
		||||
        measure {
 | 
			
		||||
            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()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        socket.parseSocketMessage("30[\"hello world\"]")
 | 
			
		||||
        manager.parseEngineMessage("30[\"hello world\"]")
 | 
			
		||||
        waitForExpectations(timeout: 3, handler: nil)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -45,8 +45,8 @@ class SocketSideEffectTest: XCTestCase {
 | 
			
		||||
            expect.fulfill()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        socket.parseSocketMessage("61-0[{\"_placeholder\":true,\"num\":0},{\"test\":true}]")
 | 
			
		||||
        socket.parseBinaryData(Data())
 | 
			
		||||
        manager.parseEngineMessage("61-0[{\"_placeholder\":true,\"num\":0},{\"test\":true}]")
 | 
			
		||||
        manager.parseEngineBinaryData(Data())
 | 
			
		||||
        waitForExpectations(timeout: 3, handler: nil)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -57,7 +57,7 @@ class SocketSideEffectTest: XCTestCase {
 | 
			
		||||
            expect.fulfill()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        socket.parseSocketMessage("2[\"test\",\"hello world\"]")
 | 
			
		||||
        manager.parseEngineMessage("2[\"test\",\"hello world\"]")
 | 
			
		||||
        waitForExpectations(timeout: 3, handler: nil)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -68,7 +68,7 @@ class SocketSideEffectTest: XCTestCase {
 | 
			
		||||
            expect.fulfill()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        socket.parseSocketMessage("2[\"test\",\"\\\"hello world\\\"\"]")
 | 
			
		||||
        manager.parseEngineMessage("2[\"test\",\"\\\"hello world\\\"\"]")
 | 
			
		||||
        waitForExpectations(timeout: 3, handler: nil)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -80,7 +80,7 @@ class SocketSideEffectTest: XCTestCase {
 | 
			
		||||
            expect.fulfill()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        socket.parseSocketMessage("2[\"test\",\"hello world\"]")
 | 
			
		||||
        manager.parseEngineMessage("2[\"test\",\"hello world\"]")
 | 
			
		||||
        waitForExpectations(timeout: 3, handler: nil)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -96,7 +96,7 @@ class SocketSideEffectTest: XCTestCase {
 | 
			
		||||
 | 
			
		||||
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
 | 
			
		||||
            // Fake connecting
 | 
			
		||||
            self.socket.parseEngineMessage("0/")
 | 
			
		||||
            self.manager.parseEngineMessage("0/")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    func testHandleBinaryEvent() {
 | 
			
		||||
        let expect = expectation(description: "handled binary event")
 | 
			
		||||
        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)
 | 
			
		||||
                expect.fulfill()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        socket.parseSocketMessage("51-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0}}]")
 | 
			
		||||
        socket.parseBinaryData(data)
 | 
			
		||||
        manager.parseEngineMessage("51-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0}}]")
 | 
			
		||||
        manager.parseEngineBinaryData(data)
 | 
			
		||||
        waitForExpectations(timeout: 3, handler: nil)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    func testHandleMultipleBinaryEvent() {
 | 
			
		||||
        let expect = expectation(description: "handled multiple binary event")
 | 
			
		||||
        socket.on("test") {data, ack in
 | 
			
		||||
            if let dict = data[0] as? NSDictionary, let data = dict["test"] as? NSData,
 | 
			
		||||
                let data2 = dict["test2"] as? NSData {
 | 
			
		||||
                    XCTAssertEqual(data as Data, self.data)
 | 
			
		||||
                    XCTAssertEqual(data2 as Data, self.data2)
 | 
			
		||||
                    expect.fulfill()
 | 
			
		||||
            if let dict = data[0] as? [String: Any], let data = dict["test"] as? Data,
 | 
			
		||||
               let data2 = dict["test2"] as? Data {
 | 
			
		||||
                XCTAssertEqual(data as Data, self.data)
 | 
			
		||||
                XCTAssertEqual(data2 as Data, self.data2)
 | 
			
		||||
                expect.fulfill()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        socket.parseSocketMessage("52-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0},\"test2\":{\"_placeholder\":true,\"num\":1}}]")
 | 
			
		||||
        socket.parseBinaryData(data)
 | 
			
		||||
        socket.parseBinaryData(data2)
 | 
			
		||||
        manager.parseEngineMessage("52-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0},\"test2\":{\"_placeholder\":true,\"num\":1}}]")
 | 
			
		||||
        manager.parseEngineBinaryData(data)
 | 
			
		||||
        manager.parseEngineBinaryData(data2)
 | 
			
		||||
        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() {
 | 
			
		||||
        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
 | 
			
		||||
            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")
 | 
			
		||||
 | 
			
		||||
                return
 | 
			
		||||
@ -251,9 +239,9 @@ class SocketSideEffectTest: XCTestCase {
 | 
			
		||||
    func testConnectTimesOutIfNotConnected() {
 | 
			
		||||
        let expect = expectation(description: "The client should call the timeout function")
 | 
			
		||||
 | 
			
		||||
        socket = manager.socket(forNamespace: "/someNamespace")
 | 
			
		||||
        socket.setTestStatus(.notConnected)
 | 
			
		||||
        socket.nsp = "/someNamespace"
 | 
			
		||||
        socket.engine = TestEngine(client: socket, url: socket.socketURL, options: nil)
 | 
			
		||||
        manager.engine = TestEngine(client: manager, url: manager.socketURL, options: nil)
 | 
			
		||||
 | 
			
		||||
        socket.connect(timeoutAfter: 0.5, withHandler: {
 | 
			
		||||
            expect.fulfill()
 | 
			
		||||
@ -266,7 +254,7 @@ class SocketSideEffectTest: XCTestCase {
 | 
			
		||||
        let expect = expectation(description: "The client should not call the timeout function")
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
            expect.fulfill()
 | 
			
		||||
@ -278,7 +266,7 @@ class SocketSideEffectTest: XCTestCase {
 | 
			
		||||
 | 
			
		||||
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
 | 
			
		||||
            // Fake connecting
 | 
			
		||||
            self.socket.parseEngineMessage("0/")
 | 
			
		||||
            self.manager.parseEngineMessage("0/")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        waitForExpectations(timeout: 2)
 | 
			
		||||
@ -288,7 +276,7 @@ class SocketSideEffectTest: XCTestCase {
 | 
			
		||||
        let expect = expectation(description: "The client call the connect handler")
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
            expect.fulfill()
 | 
			
		||||
@ -305,9 +293,9 @@ class SocketSideEffectTest: XCTestCase {
 | 
			
		||||
        let expect = expectation(description: "The client should not call the timeout function")
 | 
			
		||||
        let nspString = "/swift"
 | 
			
		||||
 | 
			
		||||
        socket = manager.socket(forNamespace: "/swift")
 | 
			
		||||
        socket.setTestStatus(.notConnected)
 | 
			
		||||
        socket.nsp = nspString
 | 
			
		||||
        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
 | 
			
		||||
            guard let nsp = data[0] as? String else {
 | 
			
		||||
@ -327,7 +315,7 @@ class SocketSideEffectTest: XCTestCase {
 | 
			
		||||
 | 
			
		||||
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
 | 
			
		||||
            // Fake connecting
 | 
			
		||||
            self.socket.parseEngineMessage("0/swift")
 | 
			
		||||
            self.manager.parseEngineMessage("0/swift")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        waitForExpectations(timeout: 2)
 | 
			
		||||
@ -377,39 +365,31 @@ class SocketSideEffectTest: XCTestCase {
 | 
			
		||||
 | 
			
		||||
    func testSettingConfigAfterInit() {
 | 
			
		||||
        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")
 | 
			
		||||
 | 
			
		||||
        socket.config = [.log(false), .nsp("/test")]
 | 
			
		||||
        manager.config = [.log(false)]
 | 
			
		||||
 | 
			
		||||
        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() {
 | 
			
		||||
        socket.setTestStatus(.notConnected)
 | 
			
		||||
        socket.config = [.extraHeaders(["new": "value"])]
 | 
			
		||||
        socket.config.insert(.extraHeaders(["hello": "world"]), replacing: true)
 | 
			
		||||
    func testSettingConfigAfterDisconnect() {
 | 
			
		||||
        socket.setTestStatus(.disconnected)
 | 
			
		||||
        manager.config.insert(.log(true))
 | 
			
		||||
 | 
			
		||||
        for config in socket.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")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
        XCTAssertTrue(DefaultSocketLogger.Logger.log, "It should set logging to true after creation")
 | 
			
		||||
 | 
			
		||||
    func testSettingConfigAfterInitWhenConnectedIgnoresChanges() {
 | 
			
		||||
        socket.config = [.log(true), .nsp("/test")]
 | 
			
		||||
        manager.config = [.log(false)]
 | 
			
		||||
 | 
			
		||||
        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() {
 | 
			
		||||
@ -419,7 +399,7 @@ class SocketSideEffectTest: XCTestCase {
 | 
			
		||||
            expect.fulfill()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        socket.engineDidSendPing()
 | 
			
		||||
        manager.engineDidSendPing()
 | 
			
		||||
 | 
			
		||||
        waitForExpectations(timeout: 0.2)
 | 
			
		||||
    }
 | 
			
		||||
@ -431,18 +411,22 @@ class SocketSideEffectTest: XCTestCase {
 | 
			
		||||
            expect.fulfill()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        socket.engineDidReceivePong()
 | 
			
		||||
        manager.engineDidReceivePong()
 | 
			
		||||
 | 
			
		||||
        waitForExpectations(timeout: 0.2)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let data = "test".data(using: String.Encoding.utf8)!
 | 
			
		||||
    let data2 = "test2".data(using: String.Encoding.utf8)!
 | 
			
		||||
 | 
			
		||||
    private var manager: SocketManager!
 | 
			
		||||
    private var socket: SocketIOClient!
 | 
			
		||||
 | 
			
		||||
    override func setUp() {
 | 
			
		||||
        super.setUp()
 | 
			
		||||
        socket = SocketIOClient(socketURL: URL(string: "http://localhost/")!)
 | 
			
		||||
 | 
			
		||||
        manager = SocketManager(socketURL: URL(string: "http://localhost/")!)
 | 
			
		||||
        socket = manager.defaultSocket
 | 
			
		||||
        socket.setTestable()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -461,6 +445,7 @@ struct ThrowingData : SocketData {
 | 
			
		||||
class TestEngine : SocketEngineSpec {
 | 
			
		||||
    weak var client: SocketEngineClient?
 | 
			
		||||
    private(set) var closed = false
 | 
			
		||||
    private(set) var compress = false
 | 
			
		||||
    private(set) var connected = false
 | 
			
		||||
    var connectParams: [String: Any]? = 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
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#import "SocketObjectiveCTest.h"
 | 
			
		||||
 | 
			
		||||
@import Dispatch;
 | 
			
		||||
@import Foundation;
 | 
			
		||||
@import XCTest;
 | 
			
		||||
@import SocketIO;
 | 
			
		||||
 | 
			
		||||
@interface SocketObjectiveCTest : XCTestCase
 | 
			
		||||
 | 
			
		||||
@property SocketIOClient* socket;
 | 
			
		||||
 | 
			
		||||
@end
 | 
			
		||||
// TODO Manager interface tests
 | 
			
		||||
 | 
			
		||||
@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 {
 | 
			
		||||
    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.reconnects = false;
 | 
			
		||||
    self.socket.reconnectWait = 1;
 | 
			
		||||
    if (self.socket.status == SocketIOClientStatusConnected) { }
 | 
			
		||||
    if (self.socket.engine == NULL) { }
 | 
			
		||||
    if (self.socket.status == SocketIOStatusConnected) { }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)testOnSyntax {
 | 
			
		||||
@ -62,7 +46,7 @@
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)testJoinNamespaceSyntax {
 | 
			
		||||
    [self.socket joinNamespace:@"/objective-c"];
 | 
			
		||||
    [self.socket joinNamespace];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)testOnAnySyntax {
 | 
			
		||||
@ -74,10 +58,6 @@
 | 
			
		||||
    }];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)testReconnectSyntax {
 | 
			
		||||
    [self.socket reconnect];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)testRemoveAllHandlersSyntax {
 | 
			
		||||
    [self.socket removeAllHandlers];
 | 
			
		||||
}
 | 
			
		||||
@ -94,15 +74,16 @@
 | 
			
		||||
    [self.socket off:@"test"];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)testSocketManager {
 | 
			
		||||
    SocketClientManager* manager = [SocketClientManager sharedManager];
 | 
			
		||||
    [manager addSocket:self.socket labeledAs:@"test"];
 | 
			
		||||
    [manager removeSocketWithLabel:@"test"];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)testSSLSecurity {
 | 
			
		||||
    SSLSecurity* sec = [[SSLSecurity alloc] initWithUsePublicKeys:0];
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
@ -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:
 | 
			
		||||
 | 
			
		||||
```swift
 | 
			
		||||
class SocketManager {
 | 
			
		||||
class Manager {
 | 
			
		||||
    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)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -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`
 | 
			
		||||
will be released and its memory reclaimed.
 | 
			
		||||
This code is **incorrect**, and the event handler will never be called. Because as soon as this method is called `manager`
 | 
			
		||||
will be released, along with the socket, and its memory reclaimed.
 | 
			
		||||
 | 
			
		||||
A correct way would be:
 | 
			
		||||
 | 
			
		||||
```swift
 | 
			
		||||
class SocketManager {
 | 
			
		||||
    let socket = SocketIOClient(socketURL: URL(string: "http://somesocketioserver.com")!)
 | 
			
		||||
class Manager {
 | 
			
		||||
    let manager = SocketManager(socketURL: URL(string: "http://somesocketioserver.com")!)
 | 
			
		||||
    
 | 
			
		||||
    func addHandlers() {
 | 
			
		||||
        socket.on("myEvent") {data, ack in
 | 
			
		||||
        manager.defaultSocket.on("myEvent") {data, ack in
 | 
			
		||||
            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