Merge branch 'development'

* development:
  Remove unnecessary function calls.
  Fix build
  Don’t create empty closures when we don’t need them. Fixes https://github.com/socketio/socket.io-client-swift/issues/1118.
  Propagate http header callback from websocket back to client.
  clean up emit completion code
  update cartfile too
  update deps: Fix #1105
  update xcode settings
  convert SocketData to serialisable socket representation
  Add a variadic method for emit completion handlers in swift
  no need to capture completion hander in wrapper
  wrap completion handler to run on handleQueue
  add optional write completion handler for emit's
This commit is contained in:
Erik Little 2018-11-28 17:08:17 -05:00
commit e4f63e8f15
No known key found for this signature in database
GPG Key ID: 62F837E56F4E9320
16 changed files with 155 additions and 68 deletions

View File

@ -1 +1 @@
github "daltoniam/Starscream" "3.0.5" github "daltoniam/Starscream" "3.0.6"

View File

@ -1,31 +1,13 @@
{ {
"object": { "object": {
"pins": [ "pins": [
{
"package": "SSCommonCrypto",
"repositoryURL": "https://github.com/daltoniam/common-crypto-spm",
"state": {
"branch": null,
"revision": "2eb3aff0fb57f92f5722fac5d6d20bf64669ca66",
"version": "1.1.0"
}
},
{ {
"package": "Starscream", "package": "Starscream",
"repositoryURL": "https://github.com/daltoniam/Starscream", "repositoryURL": "https://github.com/daltoniam/Starscream",
"state": { "state": {
"branch": null, "branch": null,
"revision": "114e5df9b6251970a069e8f1c0cbb5802759f0a9", "revision": "ebdc260ea64e68f7569c62e8744b5cd15d3a49d6",
"version": "3.0.5" "version": "3.0.6"
}
},
{
"package": "SSCZLib",
"repositoryURL": "https://github.com/daltoniam/zlib-spm.git",
"state": {
"branch": null,
"revision": "83ac8d719a2f3aa775dbdf116a57f56fb2c49abb",
"version": "1.1.0"
} }
} }
] ]

View File

@ -391,7 +391,7 @@
attributes = { attributes = {
LastSwiftMigration = 0730; LastSwiftMigration = 0730;
LastSwiftUpdateCheck = 0730; LastSwiftUpdateCheck = 0730;
LastUpgradeCheck = 0900; LastUpgradeCheck = 1000;
TargetAttributes = { TargetAttributes = {
572EF2371B51F18A00EEBB58 = { 572EF2371B51F18A00EEBB58 = {
CreatedOnToolsVersion = 6.4; CreatedOnToolsVersion = 6.4;
@ -523,18 +523,20 @@
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES; CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Developer ID Application"; CODE_SIGN_IDENTITY = "Mac Developer";
ENABLE_BITCODE = YES; ENABLE_BITCODE = YES;
"ENABLE_BITCODE[sdk=macosx*]" = NO; "ENABLE_BITCODE[sdk=macosx*]" = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
@ -596,18 +598,20 @@
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES; CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Developer ID Application"; CODE_SIGN_IDENTITY = "Mac Developer";
ENABLE_BITCODE = YES; ENABLE_BITCODE = YES;
"ENABLE_BITCODE[sdk=macosx*]" = NO; "ENABLE_BITCODE[sdk=macosx*]" = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0900" LastUpgradeVersion = "1000"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
@ -40,7 +40,6 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<Testables> <Testables>
<TestableReference <TestableReference
@ -70,7 +69,6 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"

View File

@ -204,16 +204,17 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
leaveNamespace() leaveNamespace()
} }
/// 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` /// 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]` /// will be emitted. The structure of the error data is `[eventName, items, theError]`
/// ///
/// - parameter event: The event to send. /// - parameter event: The event to send.
/// - parameter items: The items to send with this event. May be left out. /// - parameter items: The items to send with this event. May be left out.
open func emit(_ event: String, _ items: SocketData...) { /// - parameter completion: Callback called on transport write completion.
open func emit(_ event: String, _ items: SocketData..., completion: (() -> ())? = nil) {
do { do {
try emit(event, with: items.map({ try $0.socketRepresentation() })) try emit(event, with: items.map({ try $0.socketRepresentation() }), completion: completion)
} catch { } catch {
DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)", DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)",
type: logType) type: logType)
@ -231,6 +232,16 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
emit([event] + items) 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. /// 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. /// **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) 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 { guard status == .connected else {
wrappedCompletion?()
handleClientEvent(.error, data: ["Tried emitting when not connected"]) handleClientEvent(.error, data: ["Tried emitting when not connected"])
return return
} }
@ -295,7 +320,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec {
DefaultSocketLogger.Logger.log("Emitting: \(str), Ack: \(isAck)", type: logType) 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`. /// Call when you wish to tell the server that you've received the event for `ack`.

View File

@ -92,14 +92,15 @@ public protocol SocketIOClientSpec : AnyObject {
/// Disconnects the socket. /// Disconnects the socket.
func disconnect() 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` /// 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]` /// will be emitted. The structure of the error data is `[eventName, items, theError]`
/// ///
/// - parameter event: The event to send. /// - parameter event: The event to send.
/// - parameter items: The items to send with this event. May be left out. /// - 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`. /// 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 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
} }

View File

@ -49,7 +49,7 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So
/// A queue of engine.io messages waiting for POSTing /// A queue of engine.io messages waiting for POSTing
/// ///
/// **You should not touch this directly** /// **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 /// `true` if there is an outstanding poll. Trying to poll before the first is done will cause socket.io to
/// disconnect us. /// disconnect us.
@ -313,6 +313,12 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So
this.parseEngineMessage(message) this.parseEngineMessage(message)
} }
ws?.onHttpResponseHeaders = {[weak self] headers in
guard let this = self else { return }
this.client?.engineDidWebsocketUpgrade(headers: headers)
}
ws?.connect() ws?.connect()
} }
@ -340,7 +346,7 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So
if polling { if polling {
disconnectPolling(reason: reason) disconnectPolling(reason: reason)
} else { } else {
sendWebSocketMessage("", withType: .close, withData: []) sendWebSocketMessage("", withType: .close, withData: [], completion: nil)
closeOutEngine(reason: reason) 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 // 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 // Also make sure we're on the emitQueue since we're touching postWait
private func disconnectPolling(reason: String) { private func disconnectPolling(reason: String) {
postWait.append(String(SocketEnginePacketType.close.rawValue)) postWait.append((String(SocketEnginePacketType.close.rawValue), {}))
doRequest(for: createRequestForPostWithPostWait()) {_, _, _ in } doRequest(for: createRequestForPostWithPostWait()) {_, _, _ in }
closeOutEngine(reason: reason) closeOutEngine(reason: reason)
@ -366,7 +372,7 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So
DefaultSocketLogger.Logger.log("Switching to WebSockets", type: SocketEngine.logType) DefaultSocketLogger.Logger.log("Switching to WebSockets", type: SocketEngine.logType)
sendWebSocketMessage("", withType: .upgrade, withData: []) sendWebSocketMessage("", withType: .upgrade, withData: [], completion: nil)
polling = false polling = false
fastUpgrade = false fastUpgrade = false
probing = false probing = false
@ -384,7 +390,7 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So
DefaultSocketLogger.Logger.log("Flushing probe wait", type: SocketEngine.logType) DefaultSocketLogger.Logger.log("Flushing probe wait", type: SocketEngine.logType)
for waiter in probeWait { 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) probeWait.removeAll(keepingCapacity: false)
@ -398,7 +404,7 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So
guard let ws = self.ws else { return } guard let ws = self.ws else { return }
for msg in postWait { for msg in postWait {
ws.write(string: msg) ws.write(string: msg.msg, completion: msg.completion)
} }
postWait.removeAll(keepingCapacity: false) postWait.removeAll(keepingCapacity: false)
@ -544,7 +550,7 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So
} }
pongsMissed += 1 pongsMissed += 1
write("", withType: .ping, withData: []) write("", withType: .ping, withData: [], completion: nil)
engineQueue.asyncAfter(deadline: .now() + .milliseconds(pingInterval)) {[weak self, id = self.sid] in engineQueue.asyncAfter(deadline: .now() + .milliseconds(pingInterval)) {[weak self, id = self.sid] in
// Make sure not to ping old connections // 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) DefaultSocketLogger.Logger.log("Upgrading transport to WebSockets", type: SocketEngine.logType)
fastUpgrade = true fastUpgrade = true
sendPollMessage("", withType: .noop, withData: []) sendPollMessage("", withType: .noop, withData: [], completion: nil)
// After this point, we should not send anymore polling messages // 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 msg: The message to send.
/// - parameter type: The type of this message. /// - parameter type: The type of this message.
/// - parameter data: Any data that this message has. /// - 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 { engineQueue.async {
guard self.connected else { return } guard self.connected else {
completion?()
return
}
guard !self.probing else { guard !self.probing else {
self.probeWait.append((msg, type, data)) self.probeWait.append((msg, type, data, completion))
return return
} }
@ -622,11 +632,11 @@ open class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, So
if self.polling { if self.polling {
DefaultSocketLogger.Logger.log("Writing poll: \(msg) has data: \(data.count != 0)", DefaultSocketLogger.Logger.log("Writing poll: \(msg) has data: \(data.count != 0)",
type: SocketEngine.logType) type: SocketEngine.logType)
self.sendPollMessage(msg, withType: type, withData: data) self.sendPollMessage(msg, withType: type, withData: data, completion: completion)
} else { } else {
DefaultSocketLogger.Logger.log("Writing ws: \(msg) has data: \(data.count != 0)", DefaultSocketLogger.Logger.log("Writing ws: \(msg) has data: \(data.count != 0)",
type: SocketEngine.logType) 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 connected = false
polling = true 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) didError(reason: reason)
} else { } else {
client?.engineDidClose(reason: "Socket Disconnected") client?.engineDidClose(reason: "Socket Disconnected")

View File

@ -59,4 +59,9 @@ import Foundation
/// ///
/// - parameter data: The data the engine received. /// - parameter data: The data the engine received.
func parseEngineBinaryData(_ data: Data) 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])
} }

View File

@ -34,7 +34,7 @@ public protocol SocketEnginePollable : SocketEngineSpec {
/// A queue of engine.io messages waiting for POSTing /// A queue of engine.io messages waiting for POSTing
/// ///
/// **You should not touch this directly** /// **You should not touch this directly**
var postWait: [String] { get set } var postWait: [Post] { get set }
/// The URLSession that will be used for polling. /// The URLSession that will be used for polling.
var session: URLSession? { get } var session: URLSession? { get }
@ -65,7 +65,7 @@ public protocol SocketEnginePollable : SocketEngineSpec {
/// - parameter message: The message to send. /// - parameter message: The message to send.
/// - parameter withType: The type of message to send. /// - parameter withType: The type of message to send.
/// - parameter withData: The data associated with this message. /// - 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. /// Call to stop polling and invalidate the URLSession.
func stopPolling() func stopPolling()
@ -74,12 +74,15 @@ public protocol SocketEnginePollable : SocketEngineSpec {
// Default polling methods // Default polling methods
extension SocketEnginePollable { extension SocketEnginePollable {
func createRequestForPostWithPostWait() -> URLRequest { func createRequestForPostWithPostWait() -> URLRequest {
defer { postWait.removeAll(keepingCapacity: true) } defer {
for packet in postWait { packet.completion?() }
postWait.removeAll(keepingCapacity: true)
}
var postStr = "" var postStr = ""
for packet in postWait { 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") DefaultSocketLogger.Logger.log("Created POST string: \(postStr)", type: "SocketEnginePolling")
@ -215,14 +218,15 @@ extension SocketEnginePollable {
/// - parameter message: The message to send. /// - parameter message: The message to send.
/// - parameter withType: The type of message to send. /// - parameter withType: The type of message to send.
/// - parameter withData: The data associated with this message. /// - 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") 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 { for data in datas {
if case let .right(bin) = createBinaryDataForSend(using: data) { if case let .right(bin) = createBinaryDataForSend(using: data) {
postWait.append(bin) postWait.append((bin, {}))
} }
} }

View File

@ -137,7 +137,8 @@ import Starscream
/// - parameter msg: The message to send. /// - parameter msg: The message to send.
/// - parameter type: The type of this message. /// - parameter type: The type of this message.
/// - parameter data: Any data that this message has. /// - 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 { extension SocketEngineSpec {
@ -179,7 +180,7 @@ extension SocketEngineSpec {
} }
/// Send an engine message (4) /// Send an engine message (4)
func send(_ msg: String, withData datas: [Data]) { func send(_ msg: String, withData datas: [Data], completion: (() -> ())? = nil) {
write(msg, withType: .message, withData: datas) write(msg, withType: .message, withData: datas, completion: completion)
} }
} }

View File

@ -37,14 +37,18 @@ public protocol SocketEngineWebsocket : SocketEngineSpec {
/// - parameter message: The message to send. /// - parameter message: The message to send.
/// - parameter withType: The type of message to send. /// - parameter withType: The type of message to send.
/// - parameter withData: The data associated with this message. /// - 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 // WebSocket methods
extension SocketEngineWebsocket { extension SocketEngineWebsocket {
func probeWebSocket() { func probeWebSocket() {
if ws?.isConnected ?? false { 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 message: The message to send.
/// - parameter withType: The type of message to send. /// - parameter withType: The type of message to send.
/// - parameter withData: The data associated with this message. /// - 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") DefaultSocketLogger.Logger.log("Sending ws: \(str) as type: \(type.rawValue)", type: "SocketEngineWebSocket")
ws?.write(string: "\(type.rawValue)\(str)") ws?.write(string: "\(type.rawValue)\(str)")
for data in datas { for data in datas {
if case let .left(bin) = createBinaryDataForSend(using: data) { if case let .left(bin) = createBinaryDataForSend(using: data) {
ws?.write(data: bin) ws?.write(data: bin, completion: completion)
} }
} }
} }

View File

@ -286,7 +286,7 @@ open class SocketManager : NSObject, SocketManagerSpec, SocketParsable, SocketDa
/// - parameter items: The data to send with this event. /// - parameter items: The data to send with this event.
open func emitAll(_ event: String, withItems items: [Any]) { open func emitAll(_ event: String, withItems items: [Any]) {
forAll {socket in 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. /// Called when the engine has a message that must be parsed.
/// ///
/// - parameter msg: The message that needs parsing. /// - parameter msg: The message that needs parsing.

View File

@ -73,8 +73,11 @@ public typealias AckCallback = ([Any]) -> ()
/// A typealias for a normal callback. /// A typealias for a normal callback.
public typealias NormalCallback = ([Any], SocketAckEmitter) -> () public typealias NormalCallback = ([Any], SocketAckEmitter) -> ()
/// A typealias for a queued POST
public typealias Post = (msg: String, completion: (() -> ())?)
typealias JSON = [String: Any] typealias JSON = [String: Any]
typealias Probe = (msg: String, type: SocketEnginePacketType, data: [Data]) typealias Probe = (msg: String, type: SocketEnginePacketType, data: [Data], completion: (() -> ())?)
typealias ProbeWaitQueue = [Probe] typealias ProbeWaitQueue = [Probe]
enum Either<E, V> { enum Either<E, V> {

View File

@ -198,7 +198,7 @@ public class TestSocket : SocketIOClient {
super.didDisconnect(reason: reason) 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]?.fulfill()
expectations[ManagerExpectation.emitAllEventCalled] = nil expectations[ManagerExpectation.emitAllEventCalled] = nil

View File

@ -27,6 +27,11 @@ class SocketSideEffectTest: XCTestCase {
XCTAssertEqual(socket.currentAck, 1) XCTAssertEqual(socket.currentAck, 1)
} }
func testEmitCompletionSyntax() {
socket.emit("test", completion: {})
socket.emit("test", "thing", completion: {})
}
func testHandleAck() { func testHandleAck() {
let expect = expectation(description: "handled ack") let expect = expectation(description: "handled ack")
socket.emitWithAck("test").timingOut(after: 0) {data in socket.emitWithAck("test").timingOut(after: 0) {data in
@ -506,5 +511,5 @@ class TestEngine : SocketEngineSpec {
func flushWaitingForPostToWebSocket() { } func flushWaitingForPostToWebSocket() { }
func parseEngineData(_ data: Data) { } func parseEngineData(_ data: Data) { }
func parseEngineMessage(_ message: String) { } 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: (() -> ())?) { }
} }

View File

@ -67,6 +67,20 @@
[self.socket emit:@"testEmit" with:@[@YES]]; [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 { - (void)testRawEmitSyntax {
[[self.socket rawEmitView] emit:@"myEvent" with:@[@1]]; [[self.socket rawEmitView] emit:@"myEvent" with:@[@1]];
} }