handle acks from the server

This commit is contained in:
Erik 2015-02-15 13:17:36 -05:00
parent 12b8f3ef1f
commit 3754bd572a
4 changed files with 145 additions and 27 deletions

View File

@ -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. 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. 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. 3. `socket.emit(event:String, args:AnyObject...)` - Sends a message. Can send multiple args.
4. `socket.connect()` - Establishes a connection to the server. A "connect" event is fired upon successful connection. 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.close()` - Closes the socket. Once a socket is closed it should not be reopened. 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 Events
------ ------

View File

@ -24,14 +24,16 @@
import Foundation import Foundation
typealias AckCallback = ([AnyObject]?) -> Void typealias AckCallback = (AnyObject?) -> Void
class SocketAckHandler { class SocketAckHandler {
let ackNum:Int!
let event:String! let event:String!
var ackData:[AnyObject]? var ackData:[AnyObject]?
var callback:AckCallback? var callback:AckCallback?
init(event:String) { init(event:String, ackNum:Int = 0) {
self.ackNum = ackNum
self.event = event self.event = event
} }

View File

@ -25,6 +25,7 @@
import Foundation import Foundation
class SocketEvent { class SocketEvent {
let justAck:Bool!
var ack:Int? var ack:Int?
var args:AnyObject! var args:AnyObject!
lazy var currentPlace = 0 lazy var currentPlace = 0
@ -32,11 +33,12 @@ class SocketEvent {
var event:String! var event:String!
var placeholders:Int! 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.event = event
self.args = args self.args = args
self.placeholders = placeholders self.placeholders = placeholders
self.ack = ack self.ack = ackNum
self.justAck = justAck
} }
func addData(data:NSData) -> Bool { func addData(data:NSData) -> Bool {
@ -64,22 +66,38 @@ class SocketEvent {
} }
static func createMessageForEvent(event:String, withArgs args:[AnyObject], 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 message:String
var jsonSendError:NSError? var jsonSendError:NSError?
if !hasBinary { if !hasBinary {
if nsp == nil { if nsp == nil {
message = "42[\"\(event)\"," if ack == nil {
message = "42[\"\(event)\","
} else {
message = "42\(ack!)[\"\(event)\","
}
} else { } else {
message = "42/\(nsp!),[\"\(event)\"," if ack == nil {
message = "42/\(nsp!),[\"\(event)\","
} else {
message = "42/\(nsp!),\(ack!)[\"\(event)\","
}
} }
} else { } else {
if nsp == nil { if nsp == nil {
message = "45\(datas)-[\"\(event)\"," if ack == nil {
message = "45\(datas)-[\"\(event)\","
} else {
message = "45\(datas)-\(ack!)[\"\(event)\","
}
} else { } else {
message = "45\(datas)-/\(nsp!),[\"\(event)\"," if ack == nil {
message = "45\(datas)-/\(nsp!),[\"\(event)\","
} else {
message = "45\(datas)-/\(nsp!),\(ack!)[\"\(event)\","
}
} }
} }

View File

@ -36,10 +36,11 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
let emitQueue = dispatch_queue_create("emitQueue".cStringUsingEncoding(NSUTF8StringEncoding), let emitQueue = dispatch_queue_create("emitQueue".cStringUsingEncoding(NSUTF8StringEncoding),
DISPATCH_QUEUE_SERIAL) DISPATCH_QUEUE_SERIAL)
private var ackHandlers = [SocketAckHandler]() private var ackHandlers = [SocketAckHandler]()
private var secure = false private var currentAck = -1
private var handlers = [SocketEventHandler]() private var handlers = [SocketEventHandler]()
private var lastSocketMessage:SocketEvent? private var lastSocketMessage:SocketEvent?
private var pingTimer:NSTimer! private var pingTimer:NSTimer!
private var secure = false
var closed = false var closed = false
var connected = false var connected = false
var connecting = false var connecting = false
@ -126,14 +127,11 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
// Sends a message with multiple args // Sends a message with multiple args
// If a message contains binary we have to send those // If a message contains binary we have to send those
// seperately. // seperately.
func emit(event:String, _ args:AnyObject...) -> SocketAckHandler { func emit(event:String, _ args:AnyObject...) {
if !self.connected { if !self.connected {
return SocketAckHandler(event: "fail") return
} }
let ackHandler = SocketAckHandler(event: event)
self.ackHandlers.append(ackHandler)
dispatch_async(self.emitQueue) {[weak self] in dispatch_async(self.emitQueue) {[weak self] in
if self == nil { if self == nil {
return return
@ -141,11 +139,29 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
self?._emit(event, args) 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 return ackHandler
} }
private func _emit(event:String, _ args:[AnyObject]) { private func _emit(event:String, _ args:[AnyObject], ack:Bool = false) {
var frame:SocketEvent var frame:SocketEvent
var str:String var str:String
@ -156,16 +172,27 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
} }
if hasBinary { if hasBinary {
str = SocketEvent.createMessageForEvent(event, withArgs: items, if !ack {
hasBinary: true, withDatas: emitDatas.count, toNamespace: self.nsp) 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) self.io?.send(str)
for data in emitDatas { for data in emitDatas {
self.io?.send(data) self.io?.send(data)
} }
} else { } else {
str = SocketEvent.createMessageForEvent(event, withArgs: items, hasBinary: false, if !ack {
withDatas: 0, toNamespace: self.nsp) 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) 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 // Handles events
func handleEvent(event:String, data:AnyObject?, isInternalMessage:Bool = false, func handleEvent(event:String, data:AnyObject?, isInternalMessage:Bool = false,
wantsAck ack:Int? = nil, withAckType ackType:Int = 3) { wantsAck ack:Int? = nil, withAckType ackType:Int = 3) {
@ -264,7 +301,7 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
} }
// Parse an NSArray looking for binary data // 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 replacementArr = [AnyObject](count: arr.count, repeatedValue: 1)
var hasBinary = false var hasBinary = false
var arrayDatas = [NSData]() var arrayDatas = [NSData]()
@ -512,6 +549,7 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
if ackNum == "" { if ackNum == "" {
self.handleEvent(event, data: parsed) self.handleEvent(event, data: parsed)
} else { } else {
self.currentAck = ackNum.toInt()!
self.handleEvent(event, data: parsed, isInternalMessage: false, self.handleEvent(event, data: parsed, isInternalMessage: false,
wantsAck: ackNum.toInt(), withAckType: 3) wantsAck: ackNum.toInt(), withAckType: 3)
} }
@ -525,6 +563,7 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
if ackNum == "" { if ackNum == "" {
self.handleEvent(event, data: parsed) self.handleEvent(event, data: parsed)
} else { } else {
self.currentAck = ackNum.toInt()!
self.handleEvent(event, data: parsed, isInternalMessage: false, self.handleEvent(event, data: parsed, isInternalMessage: false,
wantsAck: ackNum.toInt(), withAckType: 3) wantsAck: ackNum.toInt(), withAckType: 3)
} }
@ -540,11 +579,31 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
if ackNum == "" { if ackNum == "" {
self.handleEvent(event, data: nil) self.handleEvent(event, data: nil)
} else { } else {
self.currentAck = ackNum.toInt()!
self.handleEvent(event, data: nil, isInternalMessage: false, self.handleEvent(event, data: nil, isInternalMessage: false,
wantsAck: ackNum.toInt(), withAckType: 3) wantsAck: ackNum.toInt(), withAckType: 3)
} }
return 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 End Check for message
@ -574,9 +633,13 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
/** /**
Begin check for binary placeholders 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) // println(binaryGroup)
var ackNum:String var ackNum:String
var event:String var event:String
@ -611,11 +674,35 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
mes = SocketEvent(event: event, args: placeholdersRemoved, mes = SocketEvent(event: event, args: placeholdersRemoved,
placeholders: numberOfPlaceholders.toInt()!) placeholders: numberOfPlaceholders.toInt()!)
} else { } else {
self.currentAck = ackNum.toInt()!
mes = SocketEvent(event: event, args: placeholdersRemoved, mes = SocketEvent(event: event, args: placeholdersRemoved,
placeholders: numberOfPlaceholders.toInt()!, ack: ackNum.toInt()) placeholders: numberOfPlaceholders.toInt()!, ackNum: ackNum.toInt())
} }
self.lastSocketMessage = mes 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 End check for binary placeholders
@ -634,6 +721,11 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
if let args:AnyObject = parsedArgs { if let args:AnyObject = parsedArgs {
let filledInArgs:AnyObject = self.lastSocketMessage!.fillInPlaceholders(args) let filledInArgs:AnyObject = self.lastSocketMessage!.fillInPlaceholders(args)
if self.lastSocketMessage!.justAck! {
self.handleAck(self.lastSocketMessage!.ack!, data: filledInArgs)
return
}
if self.lastSocketMessage!.ack != nil { if self.lastSocketMessage!.ack != nil {
self.handleEvent(event, data: filledInArgs, isInternalMessage: false, self.handleEvent(event, data: filledInArgs, isInternalMessage: false,
wantsAck: self.lastSocketMessage!.ack!, withAckType: 6) wantsAck: self.lastSocketMessage!.ack!, withAckType: 6)
@ -643,6 +735,11 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
} else { } else {
let filledInArgs:AnyObject = self.lastSocketMessage!.fillInPlaceholders() let filledInArgs:AnyObject = self.lastSocketMessage!.fillInPlaceholders()
if self.lastSocketMessage!.justAck! {
self.handleAck(self.lastSocketMessage!.ack!, data: filledInArgs)
return
}
if self.lastSocketMessage!.ack != nil { if self.lastSocketMessage!.ack != nil {
self.handleEvent(event, data: filledInArgs, isInternalMessage: false, self.handleEvent(event, data: filledInArgs, isInternalMessage: false,
wantsAck: self.lastSocketMessage!.ack!, withAckType: 6) wantsAck: self.lastSocketMessage!.ack!, withAckType: 6)