From 3754bd572a749ac246ec85df295de9b500e75760 Mon Sep 17 00:00:00 2001 From: Erik Date: Sun, 15 Feb 2015 13:17:36 -0500 Subject: [PATCH] handle acks from the server --- README.md | 7 +- SwiftIO/SocketAckHandler.swift | 6 +- SwiftIO/SocketEvent.swift | 32 +++++++-- SwiftIO/SocketIOClient.swift | 127 +++++++++++++++++++++++++++++---- 4 files changed, 145 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 770ff64..834cf24 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,10 @@ Methods ------- 1. `socket.on(name:String, callback:((data:AnyObject?) -> Void)) -> SocketAckHandler` - Adds a handler for an event. Returns a SocketAckHandler which can be used to ack an event. See example. 2. `socket.onMultipleItems(name:String, callback:((data:NSArray?) -> Void)) -> SocketAckHandler` - Adds a handler for an event that can have multiple items. Items are stored in an array. Returns a SocketAckHandler which can be used to ack an event. See example. -3. `socket.emit(event:String, args:AnyObject...) -> SocketAckHandler` - Sends a message. Can send multiple args. Returns a SocketAckHandler that can be used to request an ack. See example. -4. `socket.connect()` - Establishes a connection to the server. A "connect" event is fired upon successful connection. -5. `socket.close()` - Closes the socket. Once a socket is closed it should not be reopened. +3. `socket.emit(event:String, args:AnyObject...)` - Sends a message. Can send multiple args. +4. `socket.emitWithAck(event:String, args:AnyObject...) -> SocketAckHandler` - Sends a message that requests an acknoweldgement from the server. Returns a SocketAckHandler which you can use to add an onAck handler. See example. +5. `socket.connect()` - Establishes a connection to the server. A "connect" event is fired upon successful connection. +6. `socket.close()` - Closes the socket. Once a socket is closed it should not be reopened. Events ------ diff --git a/SwiftIO/SocketAckHandler.swift b/SwiftIO/SocketAckHandler.swift index c6bed5a..d3bfe77 100644 --- a/SwiftIO/SocketAckHandler.swift +++ b/SwiftIO/SocketAckHandler.swift @@ -24,14 +24,16 @@ import Foundation -typealias AckCallback = ([AnyObject]?) -> Void +typealias AckCallback = (AnyObject?) -> Void class SocketAckHandler { + let ackNum:Int! let event:String! var ackData:[AnyObject]? var callback:AckCallback? - init(event:String) { + init(event:String, ackNum:Int = 0) { + self.ackNum = ackNum self.event = event } diff --git a/SwiftIO/SocketEvent.swift b/SwiftIO/SocketEvent.swift index 3f90746..c7740c2 100644 --- a/SwiftIO/SocketEvent.swift +++ b/SwiftIO/SocketEvent.swift @@ -25,6 +25,7 @@ import Foundation class SocketEvent { + let justAck:Bool! var ack:Int? var args:AnyObject! lazy var currentPlace = 0 @@ -32,11 +33,12 @@ class SocketEvent { var event:String! var placeholders:Int! - init(event:String, args:AnyObject?, placeholders:Int = 0, ack:Int? = nil) { + init(event:String, args:AnyObject?, placeholders:Int = 0, ackNum:Int? = nil, justAck:Bool = false) { self.event = event self.args = args self.placeholders = placeholders - self.ack = ack + self.ack = ackNum + self.justAck = justAck } func addData(data:NSData) -> Bool { @@ -64,22 +66,38 @@ class SocketEvent { } static func createMessageForEvent(event:String, withArgs args:[AnyObject], - hasBinary:Bool, withDatas datas:Int = 0, toNamespace nsp:String?) -> String { + hasBinary:Bool, withDatas datas:Int = 0, toNamespace nsp:String?, wantsAck ack:Int? = nil) -> String { var message:String var jsonSendError:NSError? if !hasBinary { if nsp == nil { - message = "42[\"\(event)\"," + if ack == nil { + message = "42[\"\(event)\"," + } else { + message = "42\(ack!)[\"\(event)\"," + } } else { - message = "42/\(nsp!),[\"\(event)\"," + if ack == nil { + message = "42/\(nsp!),[\"\(event)\"," + } else { + message = "42/\(nsp!),\(ack!)[\"\(event)\"," + } } } else { if nsp == nil { - message = "45\(datas)-[\"\(event)\"," + if ack == nil { + message = "45\(datas)-[\"\(event)\"," + } else { + message = "45\(datas)-\(ack!)[\"\(event)\"," + } } else { - message = "45\(datas)-/\(nsp!),[\"\(event)\"," + if ack == nil { + message = "45\(datas)-/\(nsp!),[\"\(event)\"," + } else { + message = "45\(datas)-/\(nsp!),\(ack!)[\"\(event)\"," + } } } diff --git a/SwiftIO/SocketIOClient.swift b/SwiftIO/SocketIOClient.swift index a8e044c..523e6dc 100644 --- a/SwiftIO/SocketIOClient.swift +++ b/SwiftIO/SocketIOClient.swift @@ -36,10 +36,11 @@ class SocketIOClient: NSObject, SRWebSocketDelegate { let emitQueue = dispatch_queue_create("emitQueue".cStringUsingEncoding(NSUTF8StringEncoding), DISPATCH_QUEUE_SERIAL) private var ackHandlers = [SocketAckHandler]() - private var secure = false + private var currentAck = -1 private var handlers = [SocketEventHandler]() private var lastSocketMessage:SocketEvent? private var pingTimer:NSTimer! + private var secure = false var closed = false var connected = false var connecting = false @@ -126,14 +127,11 @@ class SocketIOClient: NSObject, SRWebSocketDelegate { // Sends a message with multiple args // If a message contains binary we have to send those // seperately. - func emit(event:String, _ args:AnyObject...) -> SocketAckHandler { + func emit(event:String, _ args:AnyObject...) { if !self.connected { - return SocketAckHandler(event: "fail") + return } - let ackHandler = SocketAckHandler(event: event) - self.ackHandlers.append(ackHandler) - dispatch_async(self.emitQueue) {[weak self] in if self == nil { return @@ -141,11 +139,29 @@ class SocketIOClient: NSObject, SRWebSocketDelegate { self?._emit(event, args) } + } + + func emitWithAck(event:String, _ args:AnyObject...) -> SocketAckHandler { + if !self.connected { + return SocketAckHandler(event: "fail") + } + + self.currentAck++ + let ackHandler = SocketAckHandler(event: event, ackNum: self.currentAck) + self.ackHandlers.append(ackHandler) + + dispatch_async(self.emitQueue) {[weak self] in + if self == nil { + return + } + + self?._emit(event, args, ack: true) + } return ackHandler } - private func _emit(event:String, _ args:[AnyObject]) { + private func _emit(event:String, _ args:[AnyObject], ack:Bool = false) { var frame:SocketEvent var str:String @@ -156,16 +172,27 @@ class SocketIOClient: NSObject, SRWebSocketDelegate { } if hasBinary { - str = SocketEvent.createMessageForEvent(event, withArgs: items, - hasBinary: true, withDatas: emitDatas.count, toNamespace: self.nsp) + if !ack { + str = SocketEvent.createMessageForEvent(event, withArgs: items, + hasBinary: true, withDatas: emitDatas.count, toNamespace: self.nsp) + } else { + str = SocketEvent.createMessageForEvent(event, withArgs: items, + hasBinary: true, withDatas: emitDatas.count, toNamespace: self.nsp, wantsAck: self.currentAck) + } self.io?.send(str) for data in emitDatas { self.io?.send(data) } } else { - str = SocketEvent.createMessageForEvent(event, withArgs: items, hasBinary: false, - withDatas: 0, toNamespace: self.nsp) + if !ack { + str = SocketEvent.createMessageForEvent(event, withArgs: items, hasBinary: false, + withDatas: 0, toNamespace: self.nsp) + } else { + str = SocketEvent.createMessageForEvent(event, withArgs: items, hasBinary: false, + withDatas: 0, toNamespace: self.nsp, wantsAck: self.currentAck) + } + self.io?.send(str) } } @@ -207,6 +234,16 @@ class SocketIOClient: NSObject, SRWebSocketDelegate { } } + // Called when the socket gets an ack for something it sent + private func handleAck(ack:Int, data:AnyObject?) { + for handler in self.ackHandlers { + if handler.ackNum == ack { + handler.callback?(data) + break + } + } + } + // Handles events func handleEvent(event:String, data:AnyObject?, isInternalMessage:Bool = false, wantsAck ack:Int? = nil, withAckType ackType:Int = 3) { @@ -264,7 +301,7 @@ class SocketIOClient: NSObject, SRWebSocketDelegate { } // Parse an NSArray looking for binary data - private class func parseArray(arr:NSArray, var placeholders:Int) -> (NSArray, Bool, [NSData]) { + private static func parseArray(arr:NSArray, var placeholders:Int) -> (NSArray, Bool, [NSData]) { var replacementArr = [AnyObject](count: arr.count, repeatedValue: 1) var hasBinary = false var arrayDatas = [NSData]() @@ -512,6 +549,7 @@ class SocketIOClient: NSObject, SRWebSocketDelegate { if ackNum == "" { self.handleEvent(event, data: parsed) } else { + self.currentAck = ackNum.toInt()! self.handleEvent(event, data: parsed, isInternalMessage: false, wantsAck: ackNum.toInt(), withAckType: 3) } @@ -525,6 +563,7 @@ class SocketIOClient: NSObject, SRWebSocketDelegate { if ackNum == "" { self.handleEvent(event, data: parsed) } else { + self.currentAck = ackNum.toInt()! self.handleEvent(event, data: parsed, isInternalMessage: false, wantsAck: ackNum.toInt(), withAckType: 3) } @@ -540,11 +579,31 @@ class SocketIOClient: NSObject, SRWebSocketDelegate { if ackNum == "" { self.handleEvent(event, data: nil) } else { + self.currentAck = ackNum.toInt()! self.handleEvent(event, data: nil, isInternalMessage: false, wantsAck: ackNum.toInt(), withAckType: 3) } return } + } else if messageGroups[1].hasPrefix("43") { + let arr = Array(messageGroups[1]) + let ackNum:String + let nsp = messageGroups[2] + + if nsp == "" && self.nsp != nil { + return + } + + if nsp == "" { + ackNum = String(arr[2...arr.count-1]) + } else { + ackNum = messageGroups[3] + } + + let ackData:AnyObject? = SocketIOClient.parseData(messageGroups[4]) + self.handleAck(ackNum.toInt()!, data: ackData) + + return } /** End Check for message @@ -574,9 +633,13 @@ class SocketIOClient: NSObject, SRWebSocketDelegate { /** Begin check for binary placeholders **/ - let binaryGroup = mutMessage["^(\\d*)-\\/?(\\w*)?,?(\\d*)?\\[(\".*?\"),(.*)\\]$"].groups() + let binaryGroup = mutMessage["^(\\d*)-\\/?(\\w*)?,?(\\d*)?\\[(\".*?\")?,?(.*)?\\]$"].groups() - if binaryGroup != nil { + if binaryGroup == nil { + return + } + + if binaryGroup[1].hasPrefix("45") { // println(binaryGroup) var ackNum:String var event:String @@ -611,11 +674,35 @@ class SocketIOClient: NSObject, SRWebSocketDelegate { mes = SocketEvent(event: event, args: placeholdersRemoved, placeholders: numberOfPlaceholders.toInt()!) } else { + self.currentAck = ackNum.toInt()! mes = SocketEvent(event: event, args: placeholdersRemoved, - placeholders: numberOfPlaceholders.toInt()!, ack: ackNum.toInt()) + placeholders: numberOfPlaceholders.toInt()!, ackNum: ackNum.toInt()) } self.lastSocketMessage = mes + } else if binaryGroup[1].hasPrefix("46") { + let messageType = RegexMutable(binaryGroup[1]) + let numberOfPlaceholders = (messageType["46"] ~= "") as String + let ackNum:String + let nsp:String + + if binaryGroup[3] == "" { + ackNum = binaryGroup[2] + nsp = "" + } else { + ackNum = binaryGroup[3] + nsp = binaryGroup[2] + } + + if nsp == "" && self.nsp != nil { + return + } + var mutMessageObject = RegexMutable(binaryGroup[5]) + let placeholdersRemoved = mutMessageObject["(\\{\"_placeholder\":true,\"num\":(\\d*)\\})"] + ~= "\"~~$2\"" + + self.lastSocketMessage = SocketEvent(event: "", args: placeholdersRemoved, + placeholders: numberOfPlaceholders.toInt()!, ackNum: ackNum.toInt(), justAck: true) } /** End check for binary placeholders @@ -634,6 +721,11 @@ class SocketIOClient: NSObject, SRWebSocketDelegate { if let args:AnyObject = parsedArgs { let filledInArgs:AnyObject = self.lastSocketMessage!.fillInPlaceholders(args) + if self.lastSocketMessage!.justAck! { + self.handleAck(self.lastSocketMessage!.ack!, data: filledInArgs) + return + } + if self.lastSocketMessage!.ack != nil { self.handleEvent(event, data: filledInArgs, isInternalMessage: false, wantsAck: self.lastSocketMessage!.ack!, withAckType: 6) @@ -643,6 +735,11 @@ class SocketIOClient: NSObject, SRWebSocketDelegate { } else { let filledInArgs:AnyObject = self.lastSocketMessage!.fillInPlaceholders() + if self.lastSocketMessage!.justAck! { + self.handleAck(self.lastSocketMessage!.ack!, data: filledInArgs) + return + } + if self.lastSocketMessage!.ack != nil { self.handleEvent(event, data: filledInArgs, isInternalMessage: false, wantsAck: self.lastSocketMessage!.ack!, withAckType: 6)