diff --git a/.travis.yml b/.travis.yml
index ff931c35..b90dafc5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,7 @@
language: objective-c
xcode_project: Socket.IO-Client-Swift.xcodeproj # path to your xcodeproj folder
xcode_scheme: SocketIO-Mac
-osx_image: xcode11.2
+osx_image: xcode12.2
branches:
only:
- master
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fa85a85f..7b496c25 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+# v16.0.0
+
+- Removed Objective-C support. It's time for you to embrace Swift.
+- Socket.io 3 support.
+
# v15.3.0
- Add `==` operators for `SocketAckStatus` and `String`
diff --git a/Cartfile b/Cartfile
index 4f886c2b..6c2bffe2 100644
--- a/Cartfile
+++ b/Cartfile
@@ -1 +1 @@
-github "daltoniam/Starscream" ~> 3.1
+github "daltoniam/Starscream" ~> 4.0
diff --git a/Cartfile.resolved b/Cartfile.resolved
index c76f7271..f9b2a1a2 100644
--- a/Cartfile.resolved
+++ b/Cartfile.resolved
@@ -1 +1 @@
-github "daltoniam/Starscream" "3.1.0"
+github "daltoniam/Starscream" "4.0.4"
diff --git a/Package.resolved b/Package.resolved
index 6f383f4c..a9c6ce6d 100644
--- a/Package.resolved
+++ b/Package.resolved
@@ -6,8 +6,8 @@
"repositoryURL": "https://github.com/daltoniam/Starscream",
"state": {
"branch": null,
- "revision": "9c03ef715d1bc9334b446c90df53586dd38cf849",
- "version": "3.1.0"
+ "revision": "df8d82047f6654d8e4b655d1b1525c64e1059d21",
+ "version": "4.0.4"
}
}
]
diff --git a/Package.swift b/Package.swift
index 001312c9..90c4b1f3 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,4 +1,4 @@
-// swift-tools-version:5.0
+// swift-tools-version:5.3
import PackageDescription
@@ -8,7 +8,7 @@ let package = Package(
.library(name: "SocketIO", targets: ["SocketIO"])
],
dependencies: [
- .package(url: "https://github.com/daltoniam/Starscream", .upToNextMinor(from: "3.1.0")),
+ .package(url: "https://github.com/daltoniam/Starscream", .upToNextMinor(from: "4.0.0")),
],
targets: [
.target(name: "SocketIO", dependencies: ["Starscream"]),
diff --git a/README.md b/README.md
index 02e9994f..ab883f9f 100644
--- a/README.md
+++ b/README.md
@@ -31,56 +31,23 @@ socket.on("currentAmount") {data, ack in
socket.connect()
```
-## Objective-C Example
-```objective-c
-@import SocketIO;
-
-NSURL* url = [[NSURL alloc] initWithString:@"http://localhost:8080"];
-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");
-}];
-
-[socket on:@"currentAmount" callback:^(NSArray* data, SocketAckEmitter* ack) {
- double cur = [[data objectAtIndex:0] floatValue];
-
- [[socket emitWithAck:@"canUpdate" with:@[@(cur)]] timingOutAfter:0 callback:^(NSArray* data) {
- if ([[data[0] description] isEqualToString:@"NO ACK"]) {
- // Handle ack timeout
- }
-
- [socket emit:@"update" with:@[@{@"amount": @(cur + 2.50)}]];
- }];
-
- [ack with:@[@"Got your currentAmount, ", @"dude"]];
-}];
-
-[socket connect];
-
-```
-
## Features
-- Supports socket.io 2.0+ (For socket.io 1.0 use v9.x)
-- Supports binary
+- Supports socket.io 2.0+/3.0+.
+- Supports Binary
- Supports Polling and WebSockets
- Supports TLS/SSL
-- Can be used from Objective-C
## FAQS
Checkout the [FAQs](https://nuclearace.github.io/Socket.IO-Client-Swift/faq.html) for commonly asked questions.
+
Checkout the [12to13](https://nuclearace.github.io/Socket.IO-Client-Swift/12to13.html) guide for migrating to v13+ from v12 below.
+Checkout the [15to16](https://nuclearace.github.io/Socket.IO-Client-Swift/15to16.html) guide for migrating to v16+ from v15.
## Installation
Requires Swift 4/5 and Xcode 10.x
-If you need Swift 2.3 use the [swift2.3 tag](https://github.com/socketio/socket.io-client-swift/releases/tag/swift2.3) (Pre-Swift 4 support is no longer maintained)
-
-If you need Swift 3.x use v11.1.3.
-
### Swift Package Manager
Add the project as a dependency to your Package.swift:
```swift
diff --git a/Socket.IO-Client-Swift.podspec b/Socket.IO-Client-Swift.podspec
index e9c9c8c0..50a988e1 100644
--- a/Socket.IO-Client-Swift.podspec
+++ b/Socket.IO-Client-Swift.podspec
@@ -1,24 +1,24 @@
Pod::Spec.new do |s|
s.name = "Socket.IO-Client-Swift"
s.module_name = "SocketIO"
- s.version = "15.2.0"
+ s.version = "16.0.0"
s.summary = "Socket.IO-client for iOS and OS X"
s.description = <<-DESC
Socket.IO-client for iOS and OS X.
Supports ws/wss/polling connections and binary.
- For socket.io 2.0+ and Swift.
+ For socket.io 3.0+ and Swift.
DESC
s.homepage = "https://github.com/socketio/socket.io-client-swift"
s.license = { :type => 'MIT' }
s.author = { "Erik" => "nuclear.ace@gmail.com" }
- s.ios.deployment_target = '8.0'
- s.osx.deployment_target = '10.10'
- s.tvos.deployment_target = '9.0'
- s.watchos.deployment_target = '2.0'
+ s.ios.deployment_target = '10.0'
+ s.osx.deployment_target = '10.13'
+ s.tvos.deployment_target = '10.0'
+ s.watchos.deployment_target = '5.0'
s.requires_arc = true
s.source = {
:git => "https://github.com/socketio/socket.io-client-swift.git",
- :tag => 'v15.2.0',
+ :tag => 'v16.0.0',
:submodules => true
}
@@ -27,5 +27,5 @@ Pod::Spec.new do |s|
'SWIFT_VERSION' => '5.0'
}
s.source_files = "Source/SocketIO/**/*.swift", "Source/SocketIO/*.swift"
- s.dependency "Starscream", "~> 3.1"
+ s.dependency "Starscream", "~> 4.0"
end
diff --git a/Socket.IO-Client-Swift.xcodeproj/project.pbxproj b/Socket.IO-Client-Swift.xcodeproj/project.pbxproj
index 21c31ee5..bb92c24a 100644
--- a/Socket.IO-Client-Swift.xcodeproj/project.pbxproj
+++ b/Socket.IO-Client-Swift.xcodeproj/project.pbxproj
@@ -18,14 +18,12 @@
1C686BE61F869AFD007D8627 /* SocketAckManagerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C686BD61F869AF1007D8627 /* SocketAckManagerTest.swift */; };
1C686BE71F869AFD007D8627 /* SocketParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C686BD71F869AF1007D8627 /* SocketParserTest.swift */; };
1C686BE81F869AFD007D8627 /* SocketNamespacePacketTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C686BD81F869AF1007D8627 /* SocketNamespacePacketTest.swift */; };
- 1C686C001F869EAE007D8627 /* SocketObjectiveCTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C686BFE1F869E9D007D8627 /* SocketObjectiveCTest.m */; };
572EF2431B51F18A00EEBB58 /* SocketIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 572EF2381B51F18A00EEBB58 /* SocketIO.framework */; };
6CA08A981D615C0B0061FD2A /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6CA08A971D615C0B0061FD2A /* Security.framework */; };
74BF53581F894326004972D8 /* SocketIO.h in Headers */ = {isa = PBXBuildFile; fileRef = 572EF23C1B51F18A00EEBB58 /* SocketIO.h */; settings = {ATTRIBUTES = (Public, ); }; };
74D0F5961F8053950037C4DC /* Starscream.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9432E00B1F77F883006AF628 /* Starscream.framework */; };
74DA21741F09440F009C19EE /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 74DA21731F09440F009C19EE /* libz.tbd */; };
74DA217C1F09457B009C19EE /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 74DA21731F09440F009C19EE /* libz.tbd */; };
- 9432E00F1F77F8C4006AF628 /* SSLSecurity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9432E0061F77F7CA006AF628 /* SSLSecurity.swift */; };
DD52B048C71D724ABBD18C71 /* SocketTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52BDC9E66AADA2CC5E8246 /* SocketTypes.swift */; };
DD52B11AF936352BAE30B2C8 /* SocketStringReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52BA240D139F72633D4159 /* SocketStringReader.swift */; };
DD52B1F8BA0455EBE7C1B93E /* SocketAckEmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52BFF2E3216CDC364BB8AF /* SocketAckEmitter.swift */; };
@@ -34,7 +32,6 @@
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 */; };
DD52B883F942CD5A9D29892B /* SocketEnginePollable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD52B2D110F55723F82B108E /* SocketEnginePollable.swift */; };
@@ -474,7 +471,6 @@
DD52BB69B6D260035B652CA4 /* SocketAnyEvent.swift in Sources */,
DD52BF924BEF05E1235CFD29 /* SocketIOClient.swift in Sources */,
DD52BFEB4DBD3BF8D93DAEFF /* SocketEventHandler.swift in Sources */,
- 9432E00F1F77F8C4006AF628 /* SSLSecurity.swift in Sources */,
DD52BB9A3E42FF2DD6BE7C2F /* SocketIOClientSpec.swift in Sources */,
DD52B2AFE7D46039C7AE4D19 /* SocketIOClientOption.swift in Sources */,
DD52BE4D1E6BB752CD9614A6 /* SocketIOStatus.swift in Sources */,
@@ -495,7 +491,6 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 1C686C001F869EAE007D8627 /* SocketObjectiveCTest.m in Sources */,
1C686BE21F869AFD007D8627 /* SocketIOClientConfigurationTest.swift in Sources */,
1C686BE31F869AFD007D8627 /* SocketEngineTest.swift in Sources */,
1C686BE41F869AFD007D8627 /* SocketSideEffectTest.swift in Sources */,
@@ -504,7 +499,6 @@
1C686BE71F869AFD007D8627 /* SocketParserTest.swift in Sources */,
1C686BE81F869AFD007D8627 /* SocketNamespacePacketTest.swift in Sources */,
DD52BCCD25EFA76E0F9B313C /* SocketMangerTest.swift in Sources */,
- DD52B53F2609D91A683DFCDD /* ManagerObjectiveCTest.m in Sources */,
1C657CDE5D510E8E2E573E39 /* utils.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -583,16 +577,16 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
- MACOSX_DEPLOYMENT_TARGET = 10.10;
+ IPHONEOS_DEPLOYMENT_TARGET = 10.0;
+ MACOSX_DEPLOYMENT_TARGET = 10.14;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = SocketIO;
SUPPORTED_PLATFORMS = "macosx appletvsimulator appletvos iphonesimulator iphoneos watchos watchsimulator";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
- TVOS_DEPLOYMENT_TARGET = 9.0;
+ TVOS_DEPLOYMENT_TARGET = 10.0;
VALID_ARCHS = "i386 x86_64 armv7 armv7s arm64 armv7k";
- WATCHOS_DEPLOYMENT_TARGET = 2.0;
+ WATCHOS_DEPLOYMENT_TARGET = 7.0;
};
name = Debug;
};
@@ -657,15 +651,15 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
- MACOSX_DEPLOYMENT_TARGET = 10.10;
+ IPHONEOS_DEPLOYMENT_TARGET = 10.0;
+ MACOSX_DEPLOYMENT_TARGET = 10.14;
PRODUCT_NAME = SocketIO;
SUPPORTED_PLATFORMS = "macosx appletvsimulator appletvos iphonesimulator iphoneos watchos watchsimulator";
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 5.0;
- TVOS_DEPLOYMENT_TARGET = 9.0;
+ TVOS_DEPLOYMENT_TARGET = 10.0;
VALID_ARCHS = "i386 x86_64 armv7 armv7s arm64 armv7k";
- WATCHOS_DEPLOYMENT_TARGET = 2.0;
+ WATCHOS_DEPLOYMENT_TARGET = 7.0;
};
name = Release;
};
diff --git a/Source/SocketIO/Client/SocketIOClient.swift b/Source/SocketIO/Client/SocketIOClient.swift
index 355fd210..160d2e9a 100644
--- a/Source/SocketIO/Client/SocketIOClient.swift
+++ b/Source/SocketIO/Client/SocketIOClient.swift
@@ -40,23 +40,14 @@ import Foundation
///
/// **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 {
+open class SocketIOClient: NSObject, SocketIOClientSpec {
// MARK: Properties
/// The namespace that this socket is currently connected to.
///
/// **Must** start with a `/`.
- @objc
public let nsp: String
- /// The session id of this client.
- @objc
- public var sid: String {
- guard let engine = manager?.engine else { return "" }
-
- return nsp == "/" ? engine.sid : "\(nsp)#\(engine.sid)"
- }
-
/// A handler that will be called on any event.
public private(set) var anyHandler: ((SocketAnyEvent) -> ())?
@@ -64,7 +55,6 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
public private(set) var handlers = [SocketEventHandler]()
/// The manager for this socket.
- @objc
public private(set) weak var manager: SocketManagerSpec?
/// A view into this socket where emits do not check for binary data.
@@ -76,18 +66,20 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
/// ```
///
/// **NOTE**: It is not safe to hold on to this view beyond the life of the socket.
- @objc
public private(set) lazy var rawEmitView = SocketRawView(socket: self)
/// The status of this client.
- @objc
public private(set) var status = SocketIOStatus.notConnected {
didSet {
handleClientEvent(.statusChange, data: [status, status.rawValue])
}
}
+ /// The id of this socket.io connect. This is different from the sid of the engine.io connection.
+ public private(set) var sid: String?
+
let ackHandlers = SocketAckManager()
+ var connectPayload: [String: Any]?
private(set) var currentAck = -1
@@ -99,7 +91,6 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
///
/// - parameter manager: The manager for this socket.
/// - parameter nsp: The namespace of the socket.
- @objc
public init(manager: SocketManagerSpec, nsp: String) {
self.manager = manager
self.nsp = nsp
@@ -117,20 +108,21 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
/// 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.
- @objc
- open func connect() {
- connect(timeoutAfter: 0, withHandler: nil)
+ ///
+ /// - parameter withPayload: An optional payload sent on connect
+ open func connect(withPayload payload: [String: Any]? = nil) {
+ connect(withPayload: payload, timeoutAfter: 0, withHandler: nil)
}
/// Connect to the server. If we aren't connected after `timeoutAfter` seconds, then `withHandler` is called.
///
/// Only call after adding your event listeners, unless you know what you're doing.
///
+ /// - parameter withPayload: An optional payload sent on connect
/// - parameter timeoutAfter: The number of seconds after which if we are not connected we assume the connection
/// has failed. Pass 0 to never timeout.
/// - parameter handler: The handler to call when the client fails to connect.
- @objc
- open func connect(timeoutAfter: Double, withHandler handler: (() -> ())?) {
+ open func connect(withPayload payload: [String: Any]? = nil, timeoutAfter: Double, withHandler handler: (() -> ())?) {
assert(timeoutAfter >= 0, "Invalid timeout: \(timeoutAfter)")
guard let manager = self.manager, status != .connected else {
@@ -140,13 +132,18 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
status = .connecting
- joinNamespace()
+ joinNamespace(withPayload: payload)
- if manager.status == .connected && nsp == "/" {
+ switch manager.version {
+ case .three:
+ break
+ case .two where manager.status == .connected && nsp == "/":
// We might not get a connect event for the default nsp, fire immediately
- didConnect(toNamespace: nsp)
+ didConnect(toNamespace: nsp, payload: nil)
return
+ case _:
+ break
}
guard timeoutAfter != 0 else { return }
@@ -171,14 +168,15 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
/// then this is only called when the client connects to that namespace.
///
/// - parameter toNamespace: The namespace that was connected to.
- open func didConnect(toNamespace namespace: String) {
+ open func didConnect(toNamespace namespace: String, payload: [String: Any]?) {
guard status != .connected else { return }
DefaultSocketLogger.Logger.log("Socket connected", type: logType)
status = .connected
+ sid = payload?["sid"] as? String
- handleClientEvent(.connect, data: [namespace])
+ handleClientEvent(.connect, data: payload == nil ? [namespace] : [namespace, payload!])
}
/// Called when the client has disconnected from socket.io.
@@ -190,6 +188,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
DefaultSocketLogger.Logger.log("Disconnected: \(reason)", type: logType)
status = .disconnected
+ sid = ""
handleClientEvent(.disconnect, data: [reason])
}
@@ -198,7 +197,6 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
///
/// 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: logType)
@@ -215,7 +213,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
/// - parameter completion: Callback called on transport write completion.
open func emit(_ event: String, _ items: SocketData..., completion: (() -> ())? = nil) {
do {
- try emit(event, with: items.map({ try $0.socketRepresentation() }), completion: completion)
+ emit([event] + (try items.map({ try $0.socketRepresentation() })), completion: completion)
} catch {
DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)",
type: logType)
@@ -224,25 +222,6 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
}
}
- /// Same as emit, but meant for Objective-C
- ///
- /// - parameter event: The event to send.
- /// - parameter items: The items to send with this event. Send an empty array to send no data.
- @objc
- open func emit(_ event: String, with items: [Any]) {
- emit([event] + items)
- }
-
- /// Same as emit, but meant for Objective-C
- ///
- /// - parameter event: The event to send.
- /// - parameter items: The items to send with this event. Send an empty array to send no data.
- /// - parameter completion: Callback called on transport write completion.
- @objc
- open func emit(_ event: String, with items: [Any], completion: (() -> ())? = nil) {
- emit([event] + items, completion: completion)
- }
-
/// Sends a message to the server, requesting an ack.
///
/// **NOTE**: It is up to the server send an ack back, just calling this method does not mean the server will ack.
@@ -264,7 +243,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
/// - returns: An `OnAckCallback`. You must call the `timingOut(after:)` method before the event will be sent.
open func emitWithAck(_ event: String, _ items: SocketData...) -> OnAckCallback {
do {
- return emitWithAck(event, with: try items.map({ try $0.socketRepresentation() }))
+ return createOnAck([event] + (try items.map({ try $0.socketRepresentation() })))
} catch {
DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)",
type: logType)
@@ -275,27 +254,6 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
}
}
- /// Same as emitWithAck, but for Objective-C
- ///
- /// **NOTE**: It is up to the server send an ack back, just calling this method does not mean the server will ack.
- /// Check that your server's api will ack the event being sent.
- ///
- /// Example:
- ///
- /// ```swift
- /// socket.emitWithAck("myEvent", with: [1]).timingOut(after: 1) {data in
- /// ...
- /// }
- /// ```
- ///
- /// - parameter event: The event to send.
- /// - parameter items: The items to send with this event. Use `[]` to send nothing.
- /// - returns: An `OnAckCallback`. You must call the `timingOut(after:)` method before the event will be sent.
- @objc
- open func emitWithAck(_ event: String, with items: [Any]) -> OnAckCallback {
- return createOnAck([event] + items)
- }
-
func emit(_ data: [Any],
ack: Int? = nil,
binary: Bool = true,
@@ -338,7 +296,6 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
///
/// - parameter ack: The number for this ack.
/// - parameter data: The data sent back with this ack.
- @objc
open func handleAck(_ ack: Int, data: [Any]) {
guard status == .connected else { return }
@@ -361,7 +318,6 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
/// - parameter data: The data that was sent with this event.
/// - parameter isInternalMessage: Whether this event was sent internally. If `true` it is always sent to handlers.
/// - parameter ack: If > 0 then this event expects to get an ack back from the client.
- @objc
open func handleEvent(_ event: String, data: [Any], isInternalMessage: Bool, withAck ack: Int = -1) {
guard status == .connected || isInternalMessage else { return }
@@ -387,7 +343,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
case .ack, .binaryAck:
handleAck(packet.id, data: packet.data)
case .connect:
- didConnect(toNamespace: nsp)
+ didConnect(toNamespace: nsp, payload: packet.data.isEmpty ? nil : packet.data[0] as? [String: Any])
case .disconnect:
didDisconnect(reason: "Got Disconnect")
case .error:
@@ -396,17 +352,19 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
}
/// Call when you wish to leave a namespace and disconnect this socket.
- @objc
open func leaveNamespace() {
manager?.disconnectSocket(self)
}
- /// Joins `nsp`.
- @objc
- open func joinNamespace() {
+ /// Joins `nsp`. You shouldn't need to call this directly, instead call `connect`.
+ ///
+ /// - parameter withPayload: An optional payload sent on connect
+ open func joinNamespace(withPayload payload: [String: Any]? = nil) {
DefaultSocketLogger.Logger.log("Joining namespace \(nsp)", type: logType)
- manager?.connectSocket(self)
+ connectPayload = payload
+
+ manager?.connectSocket(self, withPayload: connectPayload)
}
/// Removes handler(s) for a client event.
@@ -423,7 +381,6 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
/// If you wish to remove a specific event, call the `off(id:)` with the UUID received from its `on` call.
///
/// - parameter event: The event to remove handlers for.
- @objc
open func off(_ event: String) {
DefaultSocketLogger.Logger.log("Removing handler for event: \(event)", type: logType)
@@ -435,7 +392,6 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
/// If you want to remove all events for an event, call the off `off(_:)` method with the event name.
///
/// - 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: logType)
@@ -447,7 +403,6 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
/// - parameter event: The event name for this handler.
/// - parameter callback: The callback that will execute when this event is received.
/// - returns: A unique id for the handler that can be used to remove it.
- @objc
@discardableResult
open func on(_ event: String, callback: @escaping NormalCallback) -> UUID {
DefaultSocketLogger.Logger.log("Adding handler for event: \(event)", type: logType)
@@ -491,7 +446,6 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
/// - parameter event: The event name for this handler.
/// - parameter callback: The callback that will execute when this event is received.
/// - returns: A unique id for the handler that can be used to remove it.
- @objc
@discardableResult
open func once(_ event: String, callback: @escaping NormalCallback) -> UUID {
DefaultSocketLogger.Logger.log("Adding once handler for event: \(event)", type: logType)
@@ -512,20 +466,17 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
/// Adds a handler that will be called on every event.
///
/// - parameter handler: The callback that will execute whenever an event is received.
- @objc
open func onAny(_ handler: @escaping (SocketAnyEvent) -> ()) {
anyHandler = handler
}
/// Tries to reconnect to the server.
- @objc
@available(*, unavailable, message: "Call the manager's reconnect method")
open func reconnect() { }
/// Removes all handlers.
///
/// Can be used after disconnecting to break any potential remaining retain cycles.
- @objc
open func removeAllHandlers() {
handlers.removeAll(keepingCapacity: false)
}
@@ -534,7 +485,6 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
/// 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
diff --git a/Source/SocketIO/Client/SocketIOClientOption.swift b/Source/SocketIO/Client/SocketIOClientOption.swift
index bda846d0..ac1a032b 100644
--- a/Source/SocketIO/Client/SocketIOClientOption.swift
+++ b/Source/SocketIO/Client/SocketIOClientOption.swift
@@ -25,6 +25,15 @@
import Foundation
import Starscream
+/// The socket.io version being used.
+public enum SocketIOVersion: Int {
+ /// socket.io 2, engine.io 3
+ case two = 2
+
+ /// socket.io 3, engine.io 4
+ case three = 3
+}
+
protocol ClientOption : CustomStringConvertible, Equatable {
func getSocketIOOptionValue() -> Any
}
@@ -52,7 +61,7 @@ public enum SocketIOClientOption : ClientOption {
/// If passed `true`, the only transport that will be used will be WebSockets.
case forceWebsockets(Bool)
-
+
/// If passed `true`, the WebSocket stream will be configured with the enableSOCKSProxy `true`.
case enableSOCKSProxy(Bool)
@@ -80,10 +89,10 @@ public enum SocketIOClientOption : ClientOption {
/// The minimum number of seconds to wait before reconnect attempts.
case reconnectWait(Int)
-
+
/// The maximum number of seconds to wait before reconnect attempts.
case reconnectWaitMax(Int)
-
+
/// The randomization factor for calculating reconnect jitter.
case randomizationFactor(Double)
@@ -91,7 +100,7 @@ public enum SocketIOClientOption : ClientOption {
case secure(Bool)
/// Allows you to set which certs are valid. Useful for SSL pinning.
- case security(SSLSecurity)
+ case security(CertificatePinning)
/// If you're using a self-signed set. Only use for development.
case selfSigned(Bool)
@@ -99,6 +108,9 @@ public enum SocketIOClientOption : ClientOption {
/// Sets an NSURLSessionDelegate for the underlying engine. Useful if you need to handle self-signed certs.
case sessionDelegate(URLSessionDelegate)
+ /// The version of socket.io being used. This should match the server version. Default is 3.
+ case version(SocketIOVersion)
+
// MARK: Properties
/// The description of this option.
@@ -148,6 +160,8 @@ public enum SocketIOClientOption : ClientOption {
description = "sessionDelegate"
case .enableSOCKSProxy:
description = "enableSOCKSProxy"
+ case .version:
+ description = "version"
}
return description
@@ -199,6 +213,8 @@ public enum SocketIOClientOption : ClientOption {
value = delegate
case let .enableSOCKSProxy(enable):
value = enable
+ case let.version(versionNum):
+ value = versionNum
}
return value
diff --git a/Source/SocketIO/Client/SocketIOClientSpec.swift b/Source/SocketIO/Client/SocketIOClientSpec.swift
index 06c67e6e..9c0e5504 100644
--- a/Source/SocketIO/Client/SocketIOClientSpec.swift
+++ b/Source/SocketIO/Client/SocketIOClientSpec.swift
@@ -54,6 +54,9 @@ public protocol SocketIOClientSpec : AnyObject {
/// **NOTE**: It is not safe to hold on to this view beyond the life of the socket.
var rawEmitView: SocketRawView { get }
+ /// The id of this socket.io connect. This is different from the sid of the engine.io connection.
+ var sid: String? { get }
+
/// The status of this client.
var status: SocketIOStatus { get }
@@ -62,22 +65,25 @@ public protocol SocketIOClientSpec : AnyObject {
/// 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.
- func connect()
+ ///
+ /// - parameter payload: An optional payload sent on connect
+ func connect(withPayload payload: [String: Any]?)
/// Connect to the server. If we aren't connected after `timeoutAfter` seconds, then `withHandler` is called.
///
/// Only call after adding your event listeners, unless you know what you're doing.
///
+ /// - parameter withPayload: An optional payload sent on connect
/// - parameter timeoutAfter: The number of seconds after which if we are not connected we assume the connection
/// has failed. Pass 0 to never timeout.
/// - parameter handler: The handler to call when the client fails to connect.
- func connect(timeoutAfter: Double, withHandler handler: (() -> ())?)
+ func connect(withPayload payload: [String: Any]?, timeoutAfter: Double, withHandler handler: (() -> ())?)
/// Called when the client connects to a namespace. If the client was created with a namespace upfront,
/// then this is only called when the client connects to that namespace.
///
/// - parameter toNamespace: The namespace that was connected to.
- func didConnect(toNamespace namespace: String)
+ func didConnect(toNamespace namespace: String, payload: [String: Any]?)
/// Called when the client has disconnected from socket.io.
///
@@ -158,8 +164,10 @@ public protocol SocketIOClientSpec : AnyObject {
/// Call when you wish to leave a namespace and disconnect this socket.
func leaveNamespace()
- /// Joins `nsp`.
- func joinNamespace()
+ /// Joins `nsp`. You shouldn't need to call this directly, instead call `connect`.
+ ///
+ /// - Parameter withPayload: The payload to connect when joining this namespace
+ func joinNamespace(withPayload payload: [String: Any]?)
/// Removes handler(s) for a client event.
///
diff --git a/Source/SocketIO/Engine/SocketEngine.swift b/Source/SocketIO/Engine/SocketEngine.swift
index a22965f8..cd261166 100644
--- a/Source/SocketIO/Engine/SocketEngine.swift
+++ b/Source/SocketIO/Engine/SocketEngine.swift
@@ -28,7 +28,8 @@ import Starscream
/// The class that handles the engine.io protocol and transports.
/// See `SocketEnginePollable` and `SocketEngineWebsocket` for transport specific methods.
-open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, SocketEngineWebsocket, ConfigSettable {
+open class SocketEngine:
+ NSObject, WebSocketDelegate, URLSessionDelegate, SocketEnginePollable, SocketEngineWebsocket, ConfigSettable {
// MARK: Properties
private static let logType = "SocketEngine"
@@ -110,6 +111,9 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So
/// The url for WebSockets.
public private(set) var urlWebSocket = URL(string: "http://localhost/")!
+ /// The version of engine.io being used. Default is three.
+ public private(set) var version: SocketIOVersion = .three
+
/// If `true`, then the engine is currently in WebSockets mode.
@available(*, deprecated, message: "No longer needed, if we're not polling, then we must be doing websockets")
public private(set) var websocket = false
@@ -120,6 +124,9 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So
/// The WebSocket for this engine.
public private(set) var ws: WebSocket?
+ /// Whether or not the WebSocket is currently connected.
+ public private(set) var wsConnected = false
+
/// The client for this engine.
public weak var client: SocketEngineClient?
@@ -127,6 +134,7 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So
private let url: URL
+ private var lastCommunication: Date?
private var pingInterval: Int?
private var pingTimeout = 0 {
didSet {
@@ -138,7 +146,7 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So
private var pongsMissedMax = 0
private var probeWait = ProbeWaitQueue()
private var secure = false
- private var security: SocketIO.SSLSecurity?
+ private var certPinner: CertificatePinning?
private var selfSigned = false
// MARK: Initializers
@@ -197,8 +205,9 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So
}
private func handleBase64(message: String) {
+ let offset = version.rawValue >= 3 ? 1 : 2
// binary in base64 string
- let noPrefix = String(message[message.index(message.startIndex, offsetBy: 2)..= 3 {
+ checkPings()
+ } else {
+ sendPing()
+ }
if !forceWebsockets {
doPoll()
@@ -485,28 +475,55 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So
client?.engineDidReceivePong()
}
+ private func handlePing(with message: String) {
+ if version.rawValue >= 3 {
+ write("", withType: .pong, withData: [])
+ }
+
+ client?.engineDidReceivePing()
+ }
+
+ private func checkPings() {
+ let pingInterval = self.pingInterval ?? 25_000
+ let deadlineMs = Double(pingInterval + pingTimeout) / 1000
+ let timeoutDeadline = DispatchTime.now() + .milliseconds(pingInterval + pingTimeout)
+
+ engineQueue.asyncAfter(deadline: timeoutDeadline) {[weak self, id = self.sid] in
+ // Make sure not to ping old connections
+ guard let this = self, this.sid == id else { return }
+
+ if abs(this.lastCommunication?.timeIntervalSinceNow ?? deadlineMs) >= deadlineMs {
+ this.closeOutEngine(reason: "Ping timeout")
+ } else {
+ this.checkPings()
+ }
+ }
+ }
+
/// Parses raw binary received from engine.io.
///
/// - parameter data: The data to parse.
open func parseEngineData(_ data: Data) {
DefaultSocketLogger.Logger.log("Got binary data: \(data)", type: SocketEngine.logType)
- client?.parseEngineBinaryData(data.subdata(in: 1..= 3 ? data : data.subdata(in: 1..= 3 ? "b" : "b4") {
return handleBase64(message: message)
}
- guard let type = SocketEnginePacketType(rawValue: Int(reader.currentCharacter) ?? -1) else {
+ guard let type = SocketEnginePacketType(rawValue: message.first?.wholeNumberValue ?? -1) else {
checkAndHandleEngineError(message)
return
@@ -517,6 +534,8 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So
handleMessage(String(message.dropFirst()))
case .noop:
handleNOOP()
+ case .ping:
+ handlePing(with: message)
case .pong:
handlePong(with: message)
case .open:
@@ -546,7 +565,9 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So
}
private func sendPing() {
- guard connected, let pingInterval = pingInterval else { return }
+ guard connected, let pingInterval = pingInterval else {
+ return
+ }
// Server is not responding
if pongsMissed > pongsMissedMax {
@@ -559,7 +580,9 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So
engineQueue.asyncAfter(deadline: .now() + .milliseconds(pingInterval)) {[weak self, id = self.sid] in
// Make sure not to ping old connections
- guard let this = self, this.sid == id else { return }
+ guard let this = self, this.sid == id else {
+ return
+ }
this.sendPing()
}
@@ -595,12 +618,14 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So
self.secure = secure
case let .selfSigned(selfSigned):
self.selfSigned = selfSigned
- case let .security(security):
- self.security = security
+ case let .security(pinner):
+ self.certPinner = pinner
case .compress:
self.compress = true
case .enableSOCKSProxy:
self.enableSOCKSProxy = true
+ case let .version(num):
+ version = num
default:
continue
}
@@ -609,7 +634,7 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So
// Moves from long-polling to websockets
private func upgradeTransport() {
- if ws?.isConnected ?? false {
+ if wsConnected {
DefaultSocketLogger.Logger.log("Upgrading transport to WebSockets", type: SocketEngine.logType)
fastUpgrade = true
@@ -630,6 +655,7 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So
completion?()
return
}
+
guard !self.probing else {
self.probeWait.append((msg, type, data, completion))
@@ -705,3 +731,35 @@ extension SocketEngine {
didError(reason: "Engine URLSession became invalid")
}
}
+
+enum EngineError: Error {
+ case canceled
+}
+
+extension SocketEngine {
+ /// Delegate method for WebSocketDelegate.
+ ///
+ /// - Parameters:
+ /// - event: WS Event
+ /// - _:
+ public func didReceive(event: WebSocketEvent, client _: WebSocket) {
+ switch event {
+ case let .connected(headers):
+ wsConnected = true
+ client?.engineDidWebsocketUpgrade(headers: headers)
+ websocketDidConnect()
+ case .cancelled:
+ wsConnected = false
+ websocketDidDisconnect(error: EngineError.canceled)
+ case let .disconnected(reason, code):
+ wsConnected = false
+ websocketDidDisconnect(error: nil)
+ case let .text(msg):
+ parseEngineMessage(msg)
+ case let .binary(data):
+ parseEngineData(data)
+ case _:
+ break
+ }
+ }
+}
diff --git a/Source/SocketIO/Engine/SocketEngineClient.swift b/Source/SocketIO/Engine/SocketEngineClient.swift
index 00d68fa7..903fa6d8 100644
--- a/Source/SocketIO/Engine/SocketEngineClient.swift
+++ b/Source/SocketIO/Engine/SocketEngineClient.swift
@@ -44,12 +44,18 @@ import Foundation
/// - parameter reason: The reason the engine opened.
func engineDidOpen(reason: String)
- /// Called when the engine receives a pong message.
+ /// Called when the engine receives a ping message. Only called in socket.io >3.
+ func engineDidReceivePing()
+
+ /// Called when the engine receives a pong message. Only called in socket.io 2.
func engineDidReceivePong()
- /// Called when the engine sends a ping to the server.
+ /// Called when the engine sends a ping to the server. Only called in socket.io 2.
func engineDidSendPing()
+ /// Called when the engine sends a pong to the server. Only called in socket.io >3.
+ func engineDidSendPong()
+
/// Called when the engine has a message that must be parsed.
///
/// - parameter msg: The message that needs parsing.
diff --git a/Source/SocketIO/Engine/SocketEnginePacketType.swift b/Source/SocketIO/Engine/SocketEnginePacketType.swift
index b420ff2a..a3611688 100644
--- a/Source/SocketIO/Engine/SocketEnginePacketType.swift
+++ b/Source/SocketIO/Engine/SocketEnginePacketType.swift
@@ -26,7 +26,7 @@
import Foundation
/// Represents the type of engine.io packet types.
-@objc public enum SocketEnginePacketType : Int {
+@objc public enum SocketEnginePacketType: Int {
/// Open message.
case open
diff --git a/Source/SocketIO/Engine/SocketEnginePollable.swift b/Source/SocketIO/Engine/SocketEnginePollable.swift
index 9e55bd99..a5ee0736 100644
--- a/Source/SocketIO/Engine/SocketEnginePollable.swift
+++ b/Source/SocketIO/Engine/SocketEnginePollable.swift
@@ -25,7 +25,7 @@
import Foundation
/// Protocol that is used to implement socket.io polling support
-public protocol SocketEnginePollable : SocketEngineSpec {
+public protocol SocketEnginePollable: SocketEngineSpec {
// MARK: Properties
/// `true` If engine's session has been invalidated.
@@ -81,8 +81,12 @@ extension SocketEnginePollable {
var postStr = ""
- for packet in postWait {
- postStr += "\(packet.msg.utf16.count):\(packet.msg)"
+ if version.rawValue >= 3 {
+ postStr = postWait.lazy.map({ $0.msg }).joined(separator: "\u{1e}")
+ } else {
+ for packet in postWait {
+ postStr += "\(packet.msg.utf16.count):\(packet.msg)"
+ }
}
DefaultSocketLogger.Logger.log("Created POST string: \(postStr)", type: "SocketEnginePolling")
@@ -195,18 +199,32 @@ extension SocketEnginePollable {
}
func parsePollingMessage(_ str: String) {
- guard str.count != 1 else { return }
+ guard !str.isEmpty else { return }
DefaultSocketLogger.Logger.log("Got poll message: \(str)", type: "SocketEnginePolling")
- var reader = SocketStringReader(message: str)
+ if version.rawValue >= 3 {
+ let records = str.components(separatedBy: "\u{1e}")
- while reader.hasNext {
- if let n = Int(reader.readUntilOccurence(of: ":")) {
- parseEngineMessage(reader.read(count: n))
- } else {
+ for record in records {
+ parseEngineMessage(record)
+ }
+ } else {
+ guard str.count != 1 else {
parseEngineMessage(str)
- break
+
+ return
+ }
+
+ var reader = SocketStringReader(message: str)
+
+ while reader.hasNext {
+ if let n = Int(reader.readUntilOccurence(of: ":")) {
+ parseEngineMessage(reader.read(count: n))
+ } else {
+ parseEngineMessage(str)
+ break
+ }
}
}
}
diff --git a/Source/SocketIO/Engine/SocketEngineSpec.swift b/Source/SocketIO/Engine/SocketEngineSpec.swift
index a7ccf337..1eecffd3 100644
--- a/Source/SocketIO/Engine/SocketEngineSpec.swift
+++ b/Source/SocketIO/Engine/SocketEngineSpec.swift
@@ -27,7 +27,7 @@ import Foundation
import Starscream
/// Specifies a SocketEngine.
-@objc public protocol SocketEngineSpec {
+public protocol SocketEngineSpec: class {
// MARK: Properties
/// The client for this engine.
@@ -81,6 +81,9 @@ import Starscream
/// The url for WebSockets.
var urlWebSocket: URL { get }
+ /// The version of engine.io being used. Default is three.
+ var version: SocketIOVersion { get }
+
/// If `true`, then the engine is currently in WebSockets mode.
@available(*, deprecated, message: "No longer needed, if we're not polling, then we must be doing websockets")
var websocket: Bool { get }
@@ -142,10 +145,23 @@ import Starscream
}
extension SocketEngineSpec {
+ var engineIOParam: String {
+ switch version {
+ case .two:
+ return "&EIO=3"
+ case .three:
+ return "&EIO=4"
+ }
+ }
+
var urlPollingWithSid: URL {
var com = URLComponents(url: urlPolling, resolvingAgainstBaseURL: false)!
com.percentEncodedQuery = com.percentEncodedQuery! + "&sid=\(sid.urlEncode()!)"
+ if !com.percentEncodedQuery!.contains("EIO") {
+ com.percentEncodedQuery = com.percentEncodedQuery! + engineIOParam
+ }
+
return com.url!
}
@@ -153,6 +169,11 @@ extension SocketEngineSpec {
var com = URLComponents(url: urlWebSocket, resolvingAgainstBaseURL: false)!
com.percentEncodedQuery = com.percentEncodedQuery! + (sid == "" ? "" : "&sid=\(sid.urlEncode()!)")
+ if !com.percentEncodedQuery!.contains("EIO") {
+ com.percentEncodedQuery = com.percentEncodedQuery! + engineIOParam
+ }
+
+
return com.url!
}
@@ -172,10 +193,12 @@ extension SocketEngineSpec {
}
func createBinaryDataForSend(using data: Data) -> Either {
+ let prefixB64 = version.rawValue >= 3 ? "b" : "b4"
+
if polling {
- return .right("b4" + data.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0)))
+ return .right(prefixB64 + data.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0)))
} else {
- return .left(Data([0x4]) + data)
+ return .left(version.rawValue >= 3 ? data : Data([0x4]) + data)
}
}
diff --git a/Source/SocketIO/Engine/SocketEngineWebsocket.swift b/Source/SocketIO/Engine/SocketEngineWebsocket.swift
index ea1c53f9..1453ce4a 100644
--- a/Source/SocketIO/Engine/SocketEngineWebsocket.swift
+++ b/Source/SocketIO/Engine/SocketEngineWebsocket.swift
@@ -27,7 +27,12 @@ import Foundation
import Starscream
/// Protocol that is used to implement socket.io WebSocket support
-public protocol SocketEngineWebsocket : SocketEngineSpec {
+public protocol SocketEngineWebsocket: SocketEngineSpec {
+ // MARK: Properties
+
+ /// Whether or not the ws is connected
+ var wsConnected: Bool { get }
+
// MARK: Methods
/// Sends an engine.io message through the WebSocket transport.
@@ -47,7 +52,7 @@ public protocol SocketEngineWebsocket : SocketEngineSpec {
// WebSocket methods
extension SocketEngineWebsocket {
func probeWebSocket() {
- if ws?.isConnected ?? false {
+ if wsConnected {
sendWebSocketMessage("probe", withType: .ping, withData: [], completion: nil)
}
}
@@ -69,14 +74,14 @@ extension SocketEngineWebsocket {
ws?.write(string: "\(type.rawValue)\(str)")
- if data.count == 0 {
- completion?()
- }
-
for item in data {
if case let .left(bin) = createBinaryDataForSend(using: item) {
ws?.write(data: bin, completion: completion)
}
}
+
+ if data.count == 0 {
+ completion?()
+ }
}
}
diff --git a/Source/SocketIO/Manager/SocketManager.swift b/Source/SocketIO/Manager/SocketManager.swift
index f87fd7ca..c45c5f56 100644
--- a/Source/SocketIO/Manager/SocketManager.swift
+++ b/Source/SocketIO/Manager/SocketManager.swift
@@ -45,7 +45,7 @@ import Foundation
///
/// **NOTE**: The manager is not thread/queue safe, all interaction with the manager should be done on the `handleQueue`
///
-open class SocketManager : NSObject, SocketManagerSpec, SocketParsable, SocketDataBufferable, ConfigSettable {
+open class SocketManager: NSObject, SocketManagerSpec, SocketParsable, SocketDataBufferable, ConfigSettable {
private static let logType = "SocketManager"
// MARK: Properties
@@ -119,6 +119,8 @@ open class SocketManager : NSObject, SocketManagerSpec, SocketParsable, SocketDa
}
}
+ public private(set) var version = SocketIOVersion.three
+
/// 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.
@@ -202,7 +204,8 @@ open class SocketManager : NSObject, SocketManagerSpec, SocketParsable, SocketDa
/// Connects a socket through this manager's engine.
///
/// - parameter socket: The socket who we should connect through this manager.
- open func connectSocket(_ socket: SocketIOClient) {
+ /// - parameter withPayload: Optional payload to send on connect
+ open func connectSocket(_ socket: SocketIOClient, withPayload payload: [String: Any]? = nil) {
guard status == .connected else {
DefaultSocketLogger.Logger.log("Tried connecting socket when engine isn't open. Connecting",
type: SocketManager.logType)
@@ -211,7 +214,15 @@ open class SocketManager : NSObject, SocketManagerSpec, SocketParsable, SocketDa
return
}
- engine?.send("0\(socket.nsp),", withData: [])
+ var payloadStr = ""
+
+ if version.rawValue >= 3 && payload != nil,
+ let payloadData = try? JSONSerialization.data(withJSONObject: payload!, options: .fragmentsAllowed),
+ let jsonString = String(data: payloadData, encoding: .utf8) {
+ payloadStr = jsonString
+ }
+
+ engine?.send("0\(socket.nsp),\(payloadStr)", withData: [])
}
/// Called when the manager has disconnected from socket.io.
@@ -282,18 +293,8 @@ open class SocketManager : NSObject, SocketManagerSpec, SocketParsable, SocketDa
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 items: The data to send with this event.
- open func emitAll(_ event: String, withItems items: [Any]) {
forAll {socket in
- socket.emit(event, with: items, completion: nil)
+ socket.emit([event] + emitData)
}
}
@@ -349,22 +350,29 @@ open class SocketManager : NSObject, SocketManagerSpec, SocketParsable, SocketDa
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)
+ if version.rawValue < 3 {
+ nsps["/"]?.didConnect(toNamespace: "/", payload: nil)
+ }
+
+ for (nsp, socket) in nsps where socket.status == .connecting {
+ if version.rawValue < 3 && nsp == "/" {
+ continue
+ }
+
+ connectSocket(socket, withPayload: socket.connectPayload)
}
}
- /// Called when the engine receives a pong message.
- open func engineDidReceivePong() {
+ /// Called when the engine receives a ping message.
+ open func engineDidReceivePing() {
handleQueue.async {
- self._engineDidReceivePong()
+ self._engineDidReceivePing()
}
}
- private func _engineDidReceivePong() {
- emitAll(clientEvent: .pong, data: [])
+ private func _engineDidReceivePing() {
+ emitAll(clientEvent: .ping, data: [])
}
/// Called when the sends a ping to the server.
@@ -378,6 +386,28 @@ open class SocketManager : NSObject, SocketManagerSpec, SocketParsable, SocketDa
emitAll(clientEvent: .ping, data: [])
}
+ /// 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 pong to the server.
+ open func engineDidSendPong() {
+ handleQueue.async {
+ self._engineDidSendPong()
+ }
+ }
+
+ private func _engineDidSendPong() {
+ emitAll(clientEvent: .pong, data: [])
+ }
+
private func forAll(do: (SocketIOClient) throws -> ()) rethrows {
for (_, socket) in nsps {
try `do`(socket)
@@ -476,14 +506,19 @@ open class SocketManager : NSObject, SocketManagerSpec, SocketParsable, SocketDa
}
DefaultSocketLogger.Logger.log("Trying to reconnect", type: SocketManager.logType)
- emitAll(clientEvent: .reconnectAttempt, data: [(reconnectAttempts - currentReconnectAttempt)])
+
+ forAll {socket in
+ guard socket.status == .connecting else { return }
+
+ socket.handleClientEvent(.reconnectAttempt, data: [(reconnectAttempts - currentReconnectAttempt)])
+ }
currentReconnectAttempt += 1
connect()
let interval = reconnectInterval(attempts: currentReconnectAttempt)
DefaultSocketLogger.Logger.log("Scheduling reconnect in \(interval)s", type: SocketManager.logType)
- handleQueue.asyncAfter(deadline: DispatchTime.now() + interval, execute: _tryReconnect)
+ handleQueue.asyncAfter(deadline: .now() + interval, execute: _tryReconnect)
}
func reconnectInterval(attempts: Int) -> Double {
@@ -505,13 +540,13 @@ open class SocketManager : NSObject, SocketManagerSpec, SocketParsable, SocketDa
for option in config {
switch option {
case let .forceNew(new):
- self.forceNew = new
+ forceNew = new
case let .handleQueue(queue):
- self.handleQueue = queue
+ handleQueue = queue
case let .reconnects(reconnects):
self.reconnects = reconnects
case let .reconnectAttempts(attempts):
- self.reconnectAttempts = attempts
+ reconnectAttempts = attempts
case let .reconnectWait(wait):
reconnectWait = abs(wait)
case let .reconnectWaitMax(wait):
@@ -522,6 +557,8 @@ open class SocketManager : NSObject, SocketManagerSpec, SocketParsable, SocketDa
DefaultSocketLogger.Logger.log = log
case let .logger(logger):
DefaultSocketLogger.Logger = logger
+ case let .version(num):
+ version = num
case _:
continue
}
diff --git a/Source/SocketIO/Manager/SocketManagerSpec.swift b/Source/SocketIO/Manager/SocketManagerSpec.swift
index 35d5afc2..87be545e 100644
--- a/Source/SocketIO/Manager/SocketManagerSpec.swift
+++ b/Source/SocketIO/Manager/SocketManagerSpec.swift
@@ -45,7 +45,6 @@ import Foundation
/// 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 : AnyObject, SocketEngineClient {
// MARK: Properties
@@ -71,7 +70,7 @@ public protocol SocketManagerSpec : AnyObject, SocketEngineClient {
/// The minimum number of seconds to wait before attempting to reconnect.
var reconnectWait: Int { get set }
-
+
/// The maximum number of seconds to wait before attempting to reconnect.
var reconnectWaitMax: Int { get set }
@@ -84,6 +83,9 @@ public protocol SocketManagerSpec : AnyObject, SocketEngineClient {
/// The status of this manager.
var status: SocketIOStatus { get }
+ /// The version of socket.io in use.
+ var version: SocketIOVersion { get }
+
// MARK: Methods
/// Connects the underlying transport.
@@ -92,7 +94,8 @@ public protocol SocketManagerSpec : AnyObject, SocketEngineClient {
/// Connects a socket through this manager's engine.
///
/// - parameter socket: The socket who we should connect through this manager.
- func connectSocket(_ socket: SocketIOClient)
+ /// - parameter withPayload: Optional payload to send on connect
+ func connectSocket(_ socket: SocketIOClient, withPayload: [String: Any]?)
/// Called when the manager has disconnected from socket.io.
///
@@ -116,7 +119,7 @@ public protocol SocketManagerSpec : AnyObject, SocketEngineClient {
///
/// - parameter event: The event to send.
/// - parameter items: The data to send with this event.
- func emitAll(_ event: String, withItems items: [Any])
+ func emitAll(_ event: String, _ items: SocketData...)
/// Tries to reconnect to the server.
///
diff --git a/Source/SocketIO/Parse/SocketParsable.swift b/Source/SocketIO/Parse/SocketParsable.swift
index 9be9c603..4462ce2d 100644
--- a/Source/SocketIO/Parse/SocketParsable.swift
+++ b/Source/SocketIO/Parse/SocketParsable.swift
@@ -118,7 +118,7 @@ public extension SocketParsable where Self: SocketManagerSpec & SocketDataBuffer
var dataArray = String(message.utf16[message.utf16.index(reader.currentIndex, offsetBy: 1)...])!
- if type == .error && !dataArray.hasPrefix("[") && !dataArray.hasSuffix("]") {
+ if (type == .error || type == .connect) && !dataArray.hasPrefix("[") && !dataArray.hasSuffix("]") {
dataArray = "[" + dataArray + "]"
}
diff --git a/Source/SocketIO/Util/SSLSecurity.swift b/Source/SocketIO/Util/SSLSecurity.swift
deleted file mode 100644
index 2035265d..00000000
--- a/Source/SocketIO/Util/SSLSecurity.swift
+++ /dev/null
@@ -1,72 +0,0 @@
-//
-// SSLSecurity.swift
-// SocketIO-iOS
-//
-// Created by Lukas Schmidt on 24.09.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 Foundation
-import Starscream
-
-/// A wrapper around Starscream's SSLSecurity that provides a minimal Objective-C interface.
-open class SSLSecurity : NSObject {
- // MARK: Properties
-
- /// The internal Starscream SSLSecurity.
- public let security: Starscream.SSLSecurity
-
- init(security: Starscream.SSLSecurity) {
- self.security = security
- }
-
- // MARK: Methods
-
- /// Creates a new SSLSecurity that specifies whether to use publicKeys or certificates should be used for SSL
- /// pinning validation
- ///
- /// - parameter usePublicKeys: is to specific if the publicKeys or certificates should be used for SSL pinning
- /// validation
- @objc
- public convenience init(usePublicKeys: Bool = true) {
- let security = Starscream.SSLSecurity(usePublicKeys: usePublicKeys)
- self.init(security: security)
- }
-
-
- /// Designated init
- ///
- /// - parameter certs: is the certificates or public keys to use
- /// - parameter usePublicKeys: is to specific if the publicKeys or certificates should be used for SSL pinning
- /// validation
- /// - returns: a representation security object to be used with
- public convenience init(certs: [SSLCert], usePublicKeys: Bool) {
- let security = Starscream.SSLSecurity(certs: certs, usePublicKeys: usePublicKeys)
- self.init(security: security)
- }
-
- /// Returns whether or not the given trust is valid.
- ///
- /// - parameter trust: The trust to validate.
- /// - parameter domain: The CN domain to validate.
- /// - returns: Whether or not this is valid.
- public func isValid(_ trust: SecTrust, domain: String?) -> Bool {
- return security.isValid(trust, domain: domain)
- }
-}
diff --git a/Source/SocketIO/Util/SocketExtensions.swift b/Source/SocketIO/Util/SocketExtensions.swift
index 23170b79..63b0b99d 100644
--- a/Source/SocketIO/Util/SocketExtensions.swift
+++ b/Source/SocketIO/Util/SocketExtensions.swift
@@ -77,7 +77,7 @@ extension Dictionary where Key == String, Value == Any {
return .randomizationFactor(factor)
case let ("secure", secure as Bool):
return .secure(secure)
- case let ("security", security as SSLSecurity):
+ case let ("security", security as CertificatePinning):
return .security(security)
case let ("selfSigned", selfSigned as Bool):
return .selfSigned(selfSigned)
diff --git a/Tests/TestSocketIO/SocketEngineTest.swift b/Tests/TestSocketIO/SocketEngineTest.swift
index fac09d76..9f44cc86 100644
--- a/Tests/TestSocketIO/SocketEngineTest.swift
+++ b/Tests/TestSocketIO/SocketEngineTest.swift
@@ -10,13 +10,26 @@ import XCTest
@testable import SocketIO
class SocketEngineTest: XCTestCase {
+ func testBasicPollingMessageV3() {
+ let expect = expectation(description: "Basic polling test v3")
+
+ socket.on("blankTest") {data, ack in
+ expect.fulfill()
+ }
+
+ engine.setConfigs([.version(.two)])
+ engine.parsePollingMessage("15:42[\"blankTest\"]")
+
+ waitForExpectations(timeout: 3, handler: nil)
+ }
+
func testBasicPollingMessage() {
let expect = expectation(description: "Basic polling test")
socket.on("blankTest") {data, ack in
expect.fulfill()
}
- engine.parsePollingMessage("15:42[\"blankTest\"]")
+ engine.parsePollingMessage("42[\"blankTest\"]")
waitForExpectations(timeout: 3, handler: nil)
}
@@ -36,7 +49,7 @@ class SocketEngineTest: XCTestCase {
}
}
- engine.parsePollingMessage("15:42[\"blankTest\"]24:42[\"stringTest\",\"hello\"]")
+ engine.parsePollingMessage("42[\"blankTest\"]\u{1e}42[\"stringTest\",\"hello\"]")
waitForExpectations(timeout: 3, handler: nil)
}
@@ -74,7 +87,7 @@ class SocketEngineTest: XCTestCase {
let stringMessage = "42[\"stringTest\",\"lïne one\\nlīne \\rtwo𦅙𦅛\"]"
- engine.parsePollingMessage("\(stringMessage.utf16.count):\(stringMessage)")
+ engine.parsePollingMessage("\(stringMessage)")
waitForExpectations(timeout: 3, handler: nil)
}
@@ -83,20 +96,20 @@ class SocketEngineTest: XCTestCase {
"created": "2016-05-04T18:31:15+0200"
]
- XCTAssertEqual(engine.urlPolling.query, "transport=polling&b64=1&created=2016-05-04T18%3A31%3A15%2B0200")
- XCTAssertEqual(engine.urlWebSocket.query, "transport=websocket&created=2016-05-04T18%3A31%3A15%2B0200")
+ XCTAssertEqual(engine.urlPolling.query, "transport=polling&b64=1&created=2016-05-04T18%3A31%3A15%2B0200&EIO=4")
+ XCTAssertEqual(engine.urlWebSocket.query, "transport=websocket&created=2016-05-04T18%3A31%3A15%2B0200&EIO=4")
engine.connectParams = [
"forbidden": "!*'();:@&=+$,/?%#[]\" {}^|"
]
- XCTAssertEqual(engine.urlPolling.query, "transport=polling&b64=1&forbidden=%21%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%25%23%5B%5D%22%20%7B%7D%5E%7C")
- XCTAssertEqual(engine.urlWebSocket.query, "transport=websocket&forbidden=%21%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%25%23%5B%5D%22%20%7B%7D%5E%7C")
+ XCTAssertEqual(engine.urlPolling.query, "transport=polling&b64=1&forbidden=%21%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%25%23%5B%5D%22%20%7B%7D%5E%7C&EIO=4")
+ XCTAssertEqual(engine.urlWebSocket.query, "transport=websocket&forbidden=%21%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%25%23%5B%5D%22%20%7B%7D%5E%7C&EIO=4")
}
func testBase64Data() {
let expect = expectation(description: "Engine Decodes base64 data")
- let b64String = "b4aGVsbG8NCg=="
+ let b64String = "baGVsbG8NCg=="
let packetString = "451-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0}}]"
socket.on("test") {data, ack in
diff --git a/Tests/TestSocketIO/SocketMangerTest.swift b/Tests/TestSocketIO/SocketMangerTest.swift
index 453af2ea..1fa72a0a 100644
--- a/Tests/TestSocketIO/SocketMangerTest.swift
+++ b/Tests/TestSocketIO/SocketMangerTest.swift
@@ -29,7 +29,7 @@ class SocketMangerTest : XCTestCase {
XCTAssertEqual(manager.config.first!, .secure(true))
}
-
+
func testBackoffIntervalCalulation() {
XCTAssertLessThanOrEqual(manager.reconnectInterval(attempts: -1), Double(manager.reconnectWaitMax))
XCTAssertLessThanOrEqual(manager.reconnectInterval(attempts: 0), 15)
@@ -37,7 +37,7 @@ class SocketMangerTest : XCTestCase {
XCTAssertLessThanOrEqual(manager.reconnectInterval(attempts: 2), 33.75)
XCTAssertLessThanOrEqual(manager.reconnectInterval(attempts: 50), Double(manager.reconnectWaitMax))
XCTAssertLessThanOrEqual(manager.reconnectInterval(attempts: 10000), Double(manager.reconnectWaitMax))
-
+
XCTAssertGreaterThanOrEqual(manager.reconnectInterval(attempts: -1), Double(manager.reconnectWait))
XCTAssertGreaterThanOrEqual(manager.reconnectInterval(attempts: 0), Double(manager.reconnectWait))
XCTAssertGreaterThanOrEqual(manager.reconnectInterval(attempts: 1), 15)
@@ -80,24 +80,24 @@ class SocketMangerTest : XCTestCase {
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)
- }
+// 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
+// print("connect")
+// self.manager.emitAll("event", "testing")
+// }
+//
+// socket.connect()
+// socket2.connect()
+//
+// manager.fakeConnecting(toNamespace: "/swift")
+//
+// waitForExpectations(timeout: 0.3)
+// }
func testManagerSetsConfigs() {
let queue = DispatchQueue(label: "testQueue")
@@ -147,38 +147,30 @@ class SocketMangerTest : XCTestCase {
}
}
-public enum ManagerExpectation : String {
+public enum ManagerExpectation: String {
case didConnectCalled
case didDisconnectCalled
case emitAllEventCalled
}
-public class TestManager : SocketManager {
+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 func fakeConnecting(toNamespace nsp: String = "/") {
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
+ // Fake connecting
+ self.parseEngineMessage("0\(nsp)")
+ }
}
public override func socket(forNamespace nsp: String) -> SocketIOClient {
@@ -189,43 +181,25 @@ public class TestManager : SocketManager {
}
}
-public class TestSocket : SocketIOClient {
+public class TestSocket: SocketIOClient {
public var expectations = [ManagerExpectation: XCTestExpectation]()
- @objc
- public var expects = NSMutableDictionary()
-
- public override func didConnect(toNamespace nsp: String) {
+ public override func didConnect(toNamespace nsp: String, payload: [String: Any]?) {
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)
+ super.didConnect(toNamespace: nsp, payload: payload)
}
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], completion: (() -> ())?) {
+ public override func emit(_ event: String, _ items: SocketData..., completion: (() -> ())? = nil) {
expectations[ManagerExpectation.emitAllEventCalled]?.fulfill()
expectations[ManagerExpectation.emitAllEventCalled] = nil
-
- if let expect = expects[ManagerExpectation.emitAllEventCalled.rawValue] as? XCTestExpectation {
- expect.fulfill()
- expects[ManagerExpectation.emitAllEventCalled.rawValue] = nil
- }
}
}
diff --git a/Tests/TestSocketIO/SocketSideEffectTest.swift b/Tests/TestSocketIO/SocketSideEffectTest.swift
index a37f198a..ecaaee03 100644
--- a/Tests/TestSocketIO/SocketSideEffectTest.swift
+++ b/Tests/TestSocketIO/SocketSideEffectTest.swift
@@ -268,22 +268,6 @@ class SocketSideEffectTest: XCTestCase {
waitForExpectations(timeout: 0.8)
}
- func testConnectCallsConnectEventImmediatelyIfManagerAlreadyConnected() {
- let expect = expectation(description: "The client should call the connect handler")
-
- socket = manager.defaultSocket
-
- socket.setTestStatus(.notConnected)
- manager.setTestStatus(.connected)
-
- socket.on(clientEvent: .connect) {data, ack in
- expect.fulfill()
- }
- socket.connect(timeoutAfter: 0.3, withHandler: nil)
-
- waitForExpectations(timeout: 0.8)
- }
-
func testConnectDoesNotTimeOutIfConnected() {
let expect = expectation(description: "The client should not call the timeout function")
@@ -308,9 +292,14 @@ class SocketSideEffectTest: XCTestCase {
func testClientCallsConnectOnEngineOpen() {
let expect = expectation(description: "The client call the connect handler")
+ let eng = TestEngine(client: manager, url: manager.socketURL, options: nil)
+ eng.onConnect = {
+ self.socket.didConnect(toNamespace: self.socket.nsp, payload: nil)
+ }
+
+ manager.engine = eng
socket.setTestStatus(.notConnected)
- manager.engine = TestEngine(client: manager, url: manager.socketURL, options: nil)
socket.on(clientEvent: .connect) {data, ack in
expect.fulfill()
@@ -429,11 +418,11 @@ class SocketSideEffectTest: XCTestCase {
func testClientCallsSentPingHandler() {
let expect = expectation(description: "The client should emit a ping event")
- socket.on(clientEvent: .ping) {data, ack in
+ socket.on(clientEvent: .pong) {data, ack in
expect.fulfill()
}
- manager.engineDidSendPing()
+ manager.engineDidSendPong()
waitForExpectations(timeout: 0.2)
}
@@ -441,11 +430,11 @@ class SocketSideEffectTest: XCTestCase {
func testClientCallsGotPongHandler() {
let expect = expectation(description: "The client should emit a pong event")
- socket.on(clientEvent: .pong) {data, ack in
+ socket.on(clientEvent: .ping) {data, ack in
expect.fulfill()
}
- manager.engineDidReceivePong()
+ manager.engineDidReceivePing()
waitForExpectations(timeout: 0.2)
}
@@ -465,7 +454,7 @@ class SocketSideEffectTest: XCTestCase {
}
}
-struct ThrowingData : SocketData {
+struct ThrowingData: SocketData {
enum ThrowingError : Error {
case error
}
@@ -476,7 +465,7 @@ struct ThrowingData : SocketData {
}
-class TestEngine : SocketEngineSpec {
+class TestEngine: SocketEngineSpec {
weak var client: SocketEngineClient?
private(set) var closed = false
private(set) var compress = false
@@ -496,13 +485,16 @@ class TestEngine : SocketEngineSpec {
private(set) var urlWebSocket = URL(string: "http://localhost/")!
private(set) var websocket = false
private(set) var ws: WebSocket? = nil
+ private(set) var version = SocketIOVersion.three
+
+ fileprivate var onConnect: (() -> ())?
required init(client: SocketEngineClient, url: URL, options: [String: Any]?) {
self.client = client
}
func connect() {
- client?.engineDidOpen(reason: "Connect")
+ onConnect?()
}
func didError(reason: String) { }
diff --git a/Tests/TestSocketIOObjc/ManagerObjectiveCTest.h b/Tests/TestSocketIOObjc/ManagerObjectiveCTest.h
deleted file mode 100644
index e7153217..00000000
--- a/Tests/TestSocketIOObjc/ManagerObjectiveCTest.h
+++ /dev/null
@@ -1,16 +0,0 @@
-//
-// 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
diff --git a/Tests/TestSocketIOObjc/ManagerObjectiveCTest.m b/Tests/TestSocketIOObjc/ManagerObjectiveCTest.m
deleted file mode 100644
index a807eedd..00000000
--- a/Tests/TestSocketIOObjc/ManagerObjectiveCTest.m
+++ /dev/null
@@ -1,141 +0,0 @@
-//
-// Created by Erik Little on 10/21/17.
-//
-
-#import "ManagerObjectiveCTest.h"
-
-@import Dispatch;
-@import Foundation;
-@import XCTest;
-@import SocketIO;
-
-@implementation ManagerObjectiveCTest
-
-- (void)testSettingConfig {
- NSURL* url = [[NSURL alloc] initWithString:@"http://localhost"];
- NSDictionary* headers = @{@"My Header": @"Some Value"};
-
- self.manager = [[TestManager alloc] initWithSocketURL:url config:@{
- @"forceNew": @YES,
- @"extraHeaders": headers
- }];
-
- [self.manager connect];
-
- XCTAssertTrue(self.manager.forceNew);
- XCTAssertTrue([self.manager.engine.extraHeaders isEqualToDictionary:headers]);
-
-}
-
-- (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.reconnectWaitMax, 30);
- XCTAssertEqual(self.manager.randomizationFactor, 0.5);
- 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)testMangerRemoveSocket {
- [self setUpSockets];
-
- [self.manager removeSocket:self.socket];
-
- XCTAssertNil(self.manager.nsps[self.socket.nsp]);
-}
-
-- (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:@{@"log": @NO}];
- self.socket = nil;
- self.socket2 = nil;
-}
-
-@end
diff --git a/Tests/TestSocketIOObjc/SocketObjectiveCTest.h b/Tests/TestSocketIOObjc/SocketObjectiveCTest.h
deleted file mode 100644
index 1d0de3e2..00000000
--- a/Tests/TestSocketIOObjc/SocketObjectiveCTest.h
+++ /dev/null
@@ -1,16 +0,0 @@
-//
-// 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
diff --git a/Tests/TestSocketIOObjc/SocketObjectiveCTest.m b/Tests/TestSocketIOObjc/SocketObjectiveCTest.m
deleted file mode 100644
index 61412c12..00000000
--- a/Tests/TestSocketIOObjc/SocketObjectiveCTest.m
+++ /dev/null
@@ -1,122 +0,0 @@
-//
-// SocketObjectiveCTest.m
-// Socket.IO-Client-Swift
-//
-// Created by Erik Little on 3/25/16.
-//
-// Merely tests whether the Objective-C api breaks
-//
-
-#import "SocketIO_Tests-Swift.h"
-#import "SocketObjectiveCTest.h"
-
-@import Dispatch;
-@import Foundation;
-@import XCTest;
-@import SocketIO;
-
-// TODO Manager interface tests
-
-@implementation SocketObjectiveCTest
-
-- (void)testProperties {
- XCTAssertTrue([self.socket.nsp isEqualToString:@"/"]);
- XCTAssertEqual(self.socket.status, SocketIOStatusNotConnected);
-}
-
-- (void)testOnSyntax {
- [self.socket on:@"someCallback" callback:^(NSArray* data, SocketAckEmitter* ack) {
- [ack with:@[@1]];
- [[ack rawEmitView] with:@[@"hello"]];
- }];
-}
-
-- (void)testConnectSyntax {
- [self.socket connect];
-}
-
-- (void)testConnectTimeoutAfterSyntax {
- [self.socket connectWithTimeoutAfter:1 withHandler: ^() { }];
-}
-
-- (void)testDisconnectSyntax {
- [self.socket disconnect];
-}
-
-- (void)testLeaveNamespaceSyntax {
- [self.socket leaveNamespace];
-}
-
-- (void)testJoinNamespaceSyntax {
- [self.socket joinNamespace];
-}
-
-- (void)testOnAnySyntax {
- [self.socket onAny:^(SocketAnyEvent* any) {
- NSString* event = any.event;
- NSArray* data = any.items;
-
- [self.socket emit:event with:data];
- }];
-}
-
-- (void)testRemoveAllHandlersSyntax {
- [self.socket removeAllHandlers];
-}
-
-- (void)testEmitSyntax {
- [self.socket emit:@"testEmit" with:@[@YES]];
-}
-
-- (void)testEmitWriteCompletionSyntax {
- [self.socket emit:@"testEmit" with:@[@YES] completion:^{}];
-}
-
-- (void)testEmitWriteCompletion {
- XCTestExpectation* expect = [self expectationWithDescription:@"Write completion should be called"];
-
- [self.socket emit:@"testEmit" with:@[@YES] completion:^{
- [expect fulfill];
- }];
-
- [self waitForExpectationsWithTimeout:0.3 handler:nil];
-}
-
-- (void)testRawEmitSyntax {
- [[self.socket rawEmitView] emit:@"myEvent" with:@[@1]];
-}
-
-- (void)testEmitWithAckSyntax {
- [[self.socket emitWithAck:@"testAckEmit" with:@[@YES]] timingOutAfter:0 callback:^(NSArray* data) { }];
-}
-
-- (void)testOffSyntax {
- [self.socket off:@"test"];
-}
-
-- (void)testSSLSecurity {
- SSLSecurity* sec = [[SSLSecurity alloc] initWithUsePublicKeys:0];
- sec = nil;
-}
-
-- (void)testStatusChangeHandler {
- XCTestExpectation* expect = [self expectationWithDescription:@"statusChange should be correctly called"];
-
- [self.socket on:@"statusChange" callback:^(NSArray* data, SocketAckEmitter* ack) {
- XCTAssertTrue([data[1] integerValue] == SocketIOStatusConnecting);
- [expect fulfill];
- }];
-
- [OBjcUtils setTestStatusWithSocket:self.socket status:SocketIOStatusConnecting];
-
- [self waitForExpectationsWithTimeout:0.3 handler:nil];
-}
-
-- (void)setUp {
- [super setUp];
- NSURL* url = [[NSURL alloc] initWithString:@"http://localhost"];
- self.manager = [[SocketManager alloc] initWithSocketURL:url config:@{@"log": @NO}];
- self.socket = [self.manager defaultSocket];
-}
-
-@end
diff --git a/Usage Docs/15to16.md b/Usage Docs/15to16.md
new file mode 100644
index 00000000..7f425753
--- /dev/null
+++ b/Usage Docs/15to16.md
@@ -0,0 +1,16 @@
+# Upgrading from v15 to v16
+
+This guide will help you navigate the changes that were introduced in v16.
+
+## Objective-c is no longer supported. You must now use Swift.
+
+## Client supports multiple socket.io versions
+
+The client now supports socket.io 3 servers. This is mostly a transparent change, however if your sever
+is socket.io 2, you must send `.version(.two)` as an option to the manager.
+
+```swift
+SocketManager(socketURL: URL(string:"http://localhost:8087/")!, config: [.version(.two)])
+```
+
+
diff --git a/docs/12to13.html b/docs/12to13.html
index f04fe3e6..d148672c 100644
--- a/docs/12to13.html
+++ b/docs/12to13.html
@@ -20,7 +20,7 @@
+
+
+
+
+
+
+
+ SocketIO Reference
+
+ 15to16 Reference
+
+
+
+
+
+
+
+
+
+
+
+
Upgrading from v15 to v16
+
+
This guide will help you navigate the changes that were introduced in v16.
+
Objective-c is no longer supported. You must now use Swift.
+
Client supports multiple socket.io versions
+
+
The client now supports socket.io 3 servers. This is mostly a transparent change, however if your sever
+is socket.io 2, you must send .version(.two)
as an option to the manager.
+
SocketManager ( socketURL : URL ( string : "http://localhost:8087/" ) ! , config : [ . version ( . two )])
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/15to16.html b/docs/15to16.html
new file mode 100644
index 00000000..0c46a7b3
--- /dev/null
+++ b/docs/15to16.html
@@ -0,0 +1,255 @@
+
+
+