diff --git a/Cartfile.resolved b/Cartfile.resolved index c433ad3..d6801c9 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1 +1 @@ -github "daltoniam/Starscream" "3.0.5" +github "daltoniam/Starscream" "3.0.6" diff --git a/Package.resolved b/Package.resolved index 0c163c0..8a888aa 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,31 +1,13 @@ { "object": { "pins": [ - { - "package": "SSCommonCrypto", - "repositoryURL": "https://github.com/daltoniam/common-crypto-spm", - "state": { - "branch": null, - "revision": "2eb3aff0fb57f92f5722fac5d6d20bf64669ca66", - "version": "1.1.0" - } - }, { "package": "Starscream", "repositoryURL": "https://github.com/daltoniam/Starscream", "state": { "branch": null, - "revision": "114e5df9b6251970a069e8f1c0cbb5802759f0a9", - "version": "3.0.5" - } - }, - { - "package": "SSCZLib", - "repositoryURL": "https://github.com/daltoniam/zlib-spm.git", - "state": { - "branch": null, - "revision": "83ac8d719a2f3aa775dbdf116a57f56fb2c49abb", - "version": "1.1.0" + "revision": "ebdc260ea64e68f7569c62e8744b5cd15d3a49d6", + "version": "3.0.6" } } ] diff --git a/Socket.IO-Client-Swift.xcodeproj/project.pbxproj b/Socket.IO-Client-Swift.xcodeproj/project.pbxproj index 3d611e0..cf7e6e2 100644 --- a/Socket.IO-Client-Swift.xcodeproj/project.pbxproj +++ b/Socket.IO-Client-Swift.xcodeproj/project.pbxproj @@ -391,7 +391,7 @@ attributes = { LastSwiftMigration = 0730; LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0900; + LastUpgradeCheck = 1000; TargetAttributes = { 572EF2371B51F18A00EEBB58 = { CreatedOnToolsVersion = 6.4; @@ -523,18 +523,20 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "Developer ID Application"; + CODE_SIGN_IDENTITY = "Mac Developer"; ENABLE_BITCODE = YES; "ENABLE_BITCODE[sdk=macosx*]" = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -596,18 +598,20 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "Developer ID Application"; + CODE_SIGN_IDENTITY = "Mac Developer"; ENABLE_BITCODE = YES; "ENABLE_BITCODE[sdk=macosx*]" = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; diff --git a/Socket.IO-Client-Swift.xcodeproj/xcshareddata/xcschemes/SocketIO.xcscheme b/Socket.IO-Client-Swift.xcodeproj/xcshareddata/xcschemes/SocketIO.xcscheme index 74dc491..2868081 100644 --- a/Socket.IO-Client-Swift.xcodeproj/xcshareddata/xcschemes/SocketIO.xcscheme +++ b/Socket.IO-Client-Swift.xcodeproj/xcshareddata/xcschemes/SocketIO.xcscheme @@ -1,6 +1,6 @@ ())? = nil) { do { - try emit(event, with: items.map({ try $0.socketRepresentation() })) + try emit(event, with: items.map({ try $0.socketRepresentation() }), completion: completion) } catch { DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)", type: logType) @@ -231,6 +232,16 @@ open class SocketIOClient : NSObject, SocketIOClientSpec { 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. @@ -284,8 +295,22 @@ open class SocketIOClient : NSObject, SocketIOClientSpec { return createOnAck([event] + items) } - func emit(_ data: [Any], ack: Int? = nil, binary: Bool = true, isAck: Bool = false) { + func emit(_ data: [Any], + ack: Int? = nil, + binary: Bool = true, + isAck: Bool = false, + completion: (() -> ())? = nil + ) { + // wrap the completion handler so it always runs async via handlerQueue + let wrappedCompletion: (() -> ())? = (completion == nil) ? nil : {[weak self] in + guard let this = self else { return } + this.manager?.handleQueue.async { + completion!() + } + } + guard status == .connected else { + wrappedCompletion?() handleClientEvent(.error, data: ["Tried emitting when not connected"]) return } @@ -295,7 +320,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec { DefaultSocketLogger.Logger.log("Emitting: \(str), Ack: \(isAck)", type: logType) - manager?.engine?.send(str, withData: packet.binary) + manager?.engine?.send(str, withData: packet.binary, completion: wrappedCompletion) } /// Call when you wish to tell the server that you've received the event for `ack`. diff --git a/Source/SocketIO/Client/SocketIOClientSpec.swift b/Source/SocketIO/Client/SocketIOClientSpec.swift index 3835412..17cee73 100644 --- a/Source/SocketIO/Client/SocketIOClientSpec.swift +++ b/Source/SocketIO/Client/SocketIOClientSpec.swift @@ -92,14 +92,15 @@ public protocol SocketIOClientSpec : AnyObject { /// Disconnects the socket. func disconnect() - /// Send an event to the server, with optional data items. + /// Send an event to the server, with optional data items and optional write completion handler. /// /// If an error occurs trying to transform `items` into their socket representation, a `SocketClientEvent.error` /// will be emitted. The structure of the error data is `[eventName, items, theError]` /// /// - parameter event: The event to send. /// - parameter items: The items to send with this event. May be left out. - func emit(_ event: String, _ items: SocketData...) + /// - parameter completion: Callback called on transport write completion. + func emit(_ event: String, _ items: SocketData..., completion: (() -> ())?) /// Call when you wish to tell the server that you've received the event for `ack`. /// @@ -334,4 +335,16 @@ public enum SocketClientEvent : String { /// } /// ``` case statusChange + + /// Emitted when when upgrading the http connection to a websocket connection. + /// + /// Usage: + /// + /// ```swift + /// socket.on(clientEvent: .websocketUpgrade) {data, ack in + /// let headers = (data as [Any])[0] + /// // Some header logic + /// } + /// ``` + case websocketUpgrade } diff --git a/Source/SocketIO/Engine/SocketEngine.swift b/Source/SocketIO/Engine/SocketEngine.swift index 2ded52a..ee3af22 100644 --- a/Source/SocketIO/Engine/SocketEngine.swift +++ b/Source/SocketIO/Engine/SocketEngine.swift @@ -49,7 +49,7 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So /// A queue of engine.io messages waiting for POSTing /// /// **You should not touch this directly** - public var postWait = [String]() + public var postWait = [Post]() /// `true` if there is an outstanding poll. Trying to poll before the first is done will cause socket.io to /// disconnect us. @@ -313,6 +313,12 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So this.parseEngineMessage(message) } + ws?.onHttpResponseHeaders = {[weak self] headers in + guard let this = self else { return } + + this.client?.engineDidWebsocketUpgrade(headers: headers) + } + ws?.connect() } @@ -340,7 +346,7 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So if polling { disconnectPolling(reason: reason) } else { - sendWebSocketMessage("", withType: .close, withData: []) + sendWebSocketMessage("", withType: .close, withData: [], completion: nil) closeOutEngine(reason: reason) } } @@ -348,7 +354,7 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So // We need to take special care when we're polling that we send it ASAP // Also make sure we're on the emitQueue since we're touching postWait private func disconnectPolling(reason: String) { - postWait.append(String(SocketEnginePacketType.close.rawValue)) + postWait.append((String(SocketEnginePacketType.close.rawValue), {})) doRequest(for: createRequestForPostWithPostWait()) {_, _, _ in } closeOutEngine(reason: reason) @@ -366,7 +372,7 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So DefaultSocketLogger.Logger.log("Switching to WebSockets", type: SocketEngine.logType) - sendWebSocketMessage("", withType: .upgrade, withData: []) + sendWebSocketMessage("", withType: .upgrade, withData: [], completion: nil) polling = false fastUpgrade = false probing = false @@ -384,7 +390,7 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So DefaultSocketLogger.Logger.log("Flushing probe wait", type: SocketEngine.logType) for waiter in probeWait { - write(waiter.msg, withType: waiter.type, withData: waiter.data) + write(waiter.msg, withType: waiter.type, withData: waiter.data, completion: waiter.completion) } probeWait.removeAll(keepingCapacity: false) @@ -398,7 +404,7 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So guard let ws = self.ws else { return } for msg in postWait { - ws.write(string: msg) + ws.write(string: msg.msg, completion: msg.completion) } postWait.removeAll(keepingCapacity: false) @@ -544,7 +550,7 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So } pongsMissed += 1 - write("", withType: .ping, withData: []) + write("", withType: .ping, withData: [], completion: nil) engineQueue.asyncAfter(deadline: .now() + .milliseconds(pingInterval)) {[weak self, id = self.sid] in // Make sure not to ping old connections @@ -600,7 +606,7 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So DefaultSocketLogger.Logger.log("Upgrading transport to WebSockets", type: SocketEngine.logType) fastUpgrade = true - sendPollMessage("", withType: .noop, withData: []) + sendPollMessage("", withType: .noop, withData: [], completion: nil) // After this point, we should not send anymore polling messages } } @@ -610,11 +616,15 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So /// - parameter msg: The message to send. /// - parameter type: The type of this message. /// - parameter data: Any data that this message has. - open func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data]) { + /// - parameter completion: Callback called on transport write completion. + open func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data], completion: (() -> ())? = nil) { engineQueue.async { - guard self.connected else { return } + guard self.connected else { + completion?() + return + } guard !self.probing else { - self.probeWait.append((msg, type, data)) + self.probeWait.append((msg, type, data, completion)) return } @@ -622,11 +632,11 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So if self.polling { DefaultSocketLogger.Logger.log("Writing poll: \(msg) has data: \(data.count != 0)", type: SocketEngine.logType) - self.sendPollMessage(msg, withType: type, withData: data) + self.sendPollMessage(msg, withType: type, withData: data, completion: completion) } else { DefaultSocketLogger.Logger.log("Writing ws: \(msg) has data: \(data.count != 0)", type: SocketEngine.logType) - self.sendWebSocketMessage(msg, withType: type, withData: data) + self.sendWebSocketMessage(msg, withType: type, withData: data, completion: completion) } } } @@ -662,7 +672,9 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So connected = false polling = true - if let reason = error?.localizedDescription { + if let error = error as? WSError { + didError(reason: "\(error.message). code=\(error.code), type=\(error.type)") + } else if let reason = error?.localizedDescription { didError(reason: reason) } else { client?.engineDidClose(reason: "Socket Disconnected") diff --git a/Source/SocketIO/Engine/SocketEngineClient.swift b/Source/SocketIO/Engine/SocketEngineClient.swift index eefc2ac..00d68fa 100644 --- a/Source/SocketIO/Engine/SocketEngineClient.swift +++ b/Source/SocketIO/Engine/SocketEngineClient.swift @@ -59,4 +59,9 @@ import Foundation /// /// - parameter data: The data the engine received. func parseEngineBinaryData(_ data: Data) + + /// Called when when upgrading the http connection to a websocket connection. + /// + /// - parameter headers: The http headers. + func engineDidWebsocketUpgrade(headers: [String: String]) } diff --git a/Source/SocketIO/Engine/SocketEnginePollable.swift b/Source/SocketIO/Engine/SocketEnginePollable.swift index 0e46c3f..9e55bd9 100644 --- a/Source/SocketIO/Engine/SocketEnginePollable.swift +++ b/Source/SocketIO/Engine/SocketEnginePollable.swift @@ -34,7 +34,7 @@ public protocol SocketEnginePollable : SocketEngineSpec { /// A queue of engine.io messages waiting for POSTing /// /// **You should not touch this directly** - var postWait: [String] { get set } + var postWait: [Post] { get set } /// The URLSession that will be used for polling. var session: URLSession? { get } @@ -65,7 +65,7 @@ public protocol SocketEnginePollable : SocketEngineSpec { /// - parameter message: The message to send. /// - parameter withType: The type of message to send. /// - parameter withData: The data associated with this message. - func sendPollMessage(_ message: String, withType type: SocketEnginePacketType, withData datas: [Data]) + func sendPollMessage(_ message: String, withType type: SocketEnginePacketType, withData datas: [Data], completion: (() -> ())?) /// Call to stop polling and invalidate the URLSession. func stopPolling() @@ -74,12 +74,15 @@ public protocol SocketEnginePollable : SocketEngineSpec { // Default polling methods extension SocketEnginePollable { func createRequestForPostWithPostWait() -> URLRequest { - defer { postWait.removeAll(keepingCapacity: true) } + defer { + for packet in postWait { packet.completion?() } + postWait.removeAll(keepingCapacity: true) + } var postStr = "" for packet in postWait { - postStr += "\(packet.utf16.count):\(packet)" + postStr += "\(packet.msg.utf16.count):\(packet.msg)" } DefaultSocketLogger.Logger.log("Created POST string: \(postStr)", type: "SocketEnginePolling") @@ -215,14 +218,15 @@ extension SocketEnginePollable { /// - parameter message: The message to send. /// - parameter withType: The type of message to send. /// - parameter withData: The data associated with this message. - public func sendPollMessage(_ message: String, withType type: SocketEnginePacketType, withData datas: [Data]) { + /// - parameter completion: Callback called on transport write completion. + public func sendPollMessage(_ message: String, withType type: SocketEnginePacketType, withData datas: [Data], completion: (() -> ())? = nil) { DefaultSocketLogger.Logger.log("Sending poll: \(message) as type: \(type.rawValue)", type: "SocketEnginePolling") - postWait.append(String(type.rawValue) + message) + postWait.append((String(type.rawValue) + message, completion)) for data in datas { if case let .right(bin) = createBinaryDataForSend(using: data) { - postWait.append(bin) + postWait.append((bin, {})) } } diff --git a/Source/SocketIO/Engine/SocketEngineSpec.swift b/Source/SocketIO/Engine/SocketEngineSpec.swift index 4078535..2f01d74 100644 --- a/Source/SocketIO/Engine/SocketEngineSpec.swift +++ b/Source/SocketIO/Engine/SocketEngineSpec.swift @@ -137,7 +137,8 @@ import Starscream /// - parameter msg: The message to send. /// - parameter type: The type of this message. /// - parameter data: Any data that this message has. - func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data]) + /// - parameter completion: Callback called on transport write completion. + func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data], completion: (() -> ())?) } extension SocketEngineSpec { @@ -158,11 +159,11 @@ extension SocketEngineSpec { func addHeaders(to req: inout URLRequest, includingCookies additionalCookies: [HTTPCookie]? = nil) { var cookiesToAdd: [HTTPCookie] = cookies ?? [] cookiesToAdd += additionalCookies ?? [] - + if !cookiesToAdd.isEmpty { req.allHTTPHeaderFields = HTTPCookie.requestHeaderFields(with: cookiesToAdd) } - + if let extraHeaders = extraHeaders { for (headerName, value) in extraHeaders { req.setValue(value, forHTTPHeaderField: headerName) @@ -179,7 +180,7 @@ extension SocketEngineSpec { } /// Send an engine message (4) - func send(_ msg: String, withData datas: [Data]) { - write(msg, withType: .message, withData: datas) + func send(_ msg: String, withData datas: [Data], completion: (() -> ())? = nil) { + write(msg, withType: .message, withData: datas, completion: completion) } } diff --git a/Source/SocketIO/Engine/SocketEngineWebsocket.swift b/Source/SocketIO/Engine/SocketEngineWebsocket.swift index 6dc11ac..0b85ce1 100644 --- a/Source/SocketIO/Engine/SocketEngineWebsocket.swift +++ b/Source/SocketIO/Engine/SocketEngineWebsocket.swift @@ -37,14 +37,18 @@ public protocol SocketEngineWebsocket : SocketEngineSpec { /// - parameter message: The message to send. /// - parameter withType: The type of message to send. /// - parameter withData: The data associated with this message. - func sendWebSocketMessage(_ str: String, withType type: SocketEnginePacketType, withData datas: [Data]) + /// - parameter completion: Callback called on transport write completion. + func sendWebSocketMessage(_ str: String, + withType type: SocketEnginePacketType, + withData datas: [Data], + completion: (() -> ())?) } // WebSocket methods extension SocketEngineWebsocket { func probeWebSocket() { if ws?.isConnected ?? false { - sendWebSocketMessage("probe", withType: .ping, withData: []) + sendWebSocketMessage("probe", withType: .ping, withData: [], completion: nil) } } @@ -55,14 +59,19 @@ extension SocketEngineWebsocket { /// - parameter message: The message to send. /// - parameter withType: The type of message to send. /// - parameter withData: The data associated with this message. - public func sendWebSocketMessage(_ str: String, withType type: SocketEnginePacketType, withData datas: [Data]) { + /// - parameter completion: Callback called on transport write completion. + public func sendWebSocketMessage(_ str: String, + withType type: SocketEnginePacketType, + withData datas: [Data], + completion: (() -> ())? + ) { DefaultSocketLogger.Logger.log("Sending ws: \(str) as type: \(type.rawValue)", type: "SocketEngineWebSocket") ws?.write(string: "\(type.rawValue)\(str)") for data in datas { if case let .left(bin) = createBinaryDataForSend(using: data) { - ws?.write(data: bin) + ws?.write(data: bin, completion: completion) } } } diff --git a/Source/SocketIO/Manager/SocketManager.swift b/Source/SocketIO/Manager/SocketManager.swift index d4407f4..e348eed 100644 --- a/Source/SocketIO/Manager/SocketManager.swift +++ b/Source/SocketIO/Manager/SocketManager.swift @@ -286,7 +286,7 @@ open class SocketManager : NSObject, SocketManagerSpec, SocketParsable, SocketDa /// - 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) + socket.emit(event, with: items, completion: nil) } } @@ -377,6 +377,18 @@ open class SocketManager : NSObject, SocketManagerSpec, SocketParsable, SocketDa } } + /// Called when when upgrading the http connection to a websocket connection. + /// + /// - parameter headers: The http headers. + open func engineDidWebsocketUpgrade(headers: [String: String]) { + handleQueue.async { + self._engineDidWebsocketUpgrade(headers: headers) + } + } + private func _engineDidWebsocketUpgrade(headers: [String: String]) { + emitAll(clientEvent: .websocketUpgrade, data: [headers]) + } + /// Called when the engine has a message that must be parsed. /// /// - parameter msg: The message that needs parsing. diff --git a/Source/SocketIO/Util/SocketTypes.swift b/Source/SocketIO/Util/SocketTypes.swift index bbc1b6d..7bb6517 100644 --- a/Source/SocketIO/Util/SocketTypes.swift +++ b/Source/SocketIO/Util/SocketTypes.swift @@ -73,8 +73,11 @@ public typealias AckCallback = ([Any]) -> () /// A typealias for a normal callback. public typealias NormalCallback = ([Any], SocketAckEmitter) -> () +/// A typealias for a queued POST +public typealias Post = (msg: String, completion: (() -> ())?) + typealias JSON = [String: Any] -typealias Probe = (msg: String, type: SocketEnginePacketType, data: [Data]) +typealias Probe = (msg: String, type: SocketEnginePacketType, data: [Data], completion: (() -> ())?) typealias ProbeWaitQueue = [Probe] enum Either { diff --git a/Tests/TestSocketIO/SocketMangerTest.swift b/Tests/TestSocketIO/SocketMangerTest.swift index 37e5dba..8041948 100644 --- a/Tests/TestSocketIO/SocketMangerTest.swift +++ b/Tests/TestSocketIO/SocketMangerTest.swift @@ -198,7 +198,7 @@ public class TestSocket : SocketIOClient { super.didDisconnect(reason: reason) } - public override func emit(_ event: String, with items: [Any]) { + public override func emit(_ event: String, with items: [Any], completion: (() -> ())?) { expectations[ManagerExpectation.emitAllEventCalled]?.fulfill() expectations[ManagerExpectation.emitAllEventCalled] = nil diff --git a/Tests/TestSocketIO/SocketSideEffectTest.swift b/Tests/TestSocketIO/SocketSideEffectTest.swift index 5c8a77d..a37f198 100644 --- a/Tests/TestSocketIO/SocketSideEffectTest.swift +++ b/Tests/TestSocketIO/SocketSideEffectTest.swift @@ -27,6 +27,11 @@ class SocketSideEffectTest: XCTestCase { XCTAssertEqual(socket.currentAck, 1) } + func testEmitCompletionSyntax() { + socket.emit("test", completion: {}) + socket.emit("test", "thing", completion: {}) + } + func testHandleAck() { let expect = expectation(description: "handled ack") socket.emitWithAck("test").timingOut(after: 0) {data in @@ -506,5 +511,5 @@ class TestEngine : SocketEngineSpec { func flushWaitingForPostToWebSocket() { } func parseEngineData(_ data: Data) { } func parseEngineMessage(_ message: String) { } - func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data]) { } + func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data], completion: (() -> ())?) { } } diff --git a/Tests/TestSocketIOObjc/SocketObjectiveCTest.m b/Tests/TestSocketIOObjc/SocketObjectiveCTest.m index 1ad3475..28c21b9 100644 --- a/Tests/TestSocketIOObjc/SocketObjectiveCTest.m +++ b/Tests/TestSocketIOObjc/SocketObjectiveCTest.m @@ -67,6 +67,20 @@ [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]]; }