add new things to 1.2
This commit is contained in:
parent
02b1314a3d
commit
1ac05d9790
22
README.md
22
README.md
@ -1,9 +1,9 @@
|
||||
Socket.IO-Client-Swift
|
||||
======================
|
||||
|
||||
Socket.IO-client for Swift. Supports ws/wss connections and binary. For socket.io 1.0+ and Swift 1.2.
|
||||
Socket.IO-client for Swift. Supports ws/wss/polling connections and binary. For socket.io 1.0+ and Swift 1.2.
|
||||
|
||||
For Swift 1.1 use the master branch.
|
||||
For Swift 1.1 use the 1.1 branch.
|
||||
|
||||
Installation
|
||||
============
|
||||
@ -15,14 +15,15 @@ API
|
||||
===
|
||||
Constructor
|
||||
-----------
|
||||
`init(socketURL: String, opts:[String: AnyObject]? = nil)` - Constructs a new client for the given URL. opts can be omitted (will use default values.)
|
||||
`init(socketURL: String, opts:[String: AnyObject]? = nil)` - Constructs a new client for the given URL. opts can be omitted (will use default values. See example)
|
||||
Methods
|
||||
-------
|
||||
1. `socket.on(name:String, callback:((data:NSArray?, ack:AckEmitter?) -> Void))` - Adds a handler for an event. Items are passed by an array. `ack` can be used to send an ack when one is requested. See example.
|
||||
2. `socket.onAny(callback:((event:String, items:AnyObject?)) -> Void)` - Adds a handler for all events. It will be called on any received event.
|
||||
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.
|
||||
4. `socket.emitWithAck(event:String, args:AnyObject...) -> SocketAckHandler` - Sends a message that requests an acknowledgement 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.connectWithParams(params:[String: AnyObject])` - Establishes a connection to the server, passing the specified params. A "connect" event is fired upon successful connection.
|
||||
6. `socket.connectWithParams(params:[String: AnyObject])` - Establishes a connection to the server passing the specified params. A "connect" event is fired upon successful connection.
|
||||
7. `socket.close()` - Closes the socket. Once a socket is closed it should not be reopened.
|
||||
|
||||
Events
|
||||
@ -41,9 +42,13 @@ let socket = SocketIOClient(socketURL: "https://localhost:8080", opts: [
|
||||
"reconnects": true, // default true
|
||||
"reconnectAttempts": 5, // default -1 (infinite tries)
|
||||
"reconnectWait": 5, // default 10
|
||||
"nsp": "swift" // connects to the specified namespace. Default is /
|
||||
"nsp": "swift", // connects to the specified namespace. Default is /
|
||||
"forcePolling": true // if true, the socket will only use XHR polling, default is false (polling/WebSockets)
|
||||
])
|
||||
|
||||
// Called on every event
|
||||
socket.onAny {println("got event: \($0.event) with items \($0.items)")}
|
||||
|
||||
// Socket Events
|
||||
socket.on("connect") {data, ack in
|
||||
println("socket connected")
|
||||
@ -67,7 +72,7 @@ socket.on("ackEvent") {data, ack in
|
||||
}
|
||||
|
||||
socket.emitWithAck("ackTest", "test").onAck {data in
|
||||
println(data)
|
||||
println(data?[0])
|
||||
}
|
||||
|
||||
ack?("Got your event", "dude")
|
||||
@ -98,8 +103,7 @@ socket.on("jsonTest") {data, ack in
|
||||
}
|
||||
}
|
||||
|
||||
// Messages that have multiple items are passed
|
||||
// by an array
|
||||
// Event items are passed by an array
|
||||
socket.on("multipleItems") {data, ack in
|
||||
if data == nil {
|
||||
return
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
typealias AckCallback = (AnyObject?) -> Void
|
||||
typealias AckCallback = (NSArray?) -> Void
|
||||
|
||||
class SocketAckHandler {
|
||||
let ackNum:Int!
|
||||
|
||||
596
SwiftIO/SocketEngine.swift
Normal file
596
SwiftIO/SocketEngine.swift
Normal file
@ -0,0 +1,596 @@
|
||||
//
|
||||
// SocketEngine.swift
|
||||
// Socket.IO-Swift
|
||||
//
|
||||
// Created by Erik Little on 3/3/15.
|
||||
//
|
||||
// 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
|
||||
|
||||
// This is used because in Swift 1.1, turning on -O causes a
|
||||
// memory access violation in SocketEngine#parseEngineMessage
|
||||
private var fixSwift:AnyObject?
|
||||
|
||||
extension String {
|
||||
private var length:Int {
|
||||
return count(self)
|
||||
}
|
||||
}
|
||||
|
||||
private typealias PollWaitQueue = [() -> Void]
|
||||
|
||||
private enum PacketType: String {
|
||||
case OPEN = "0"
|
||||
case CLOSE = "1"
|
||||
case PING = "2"
|
||||
case PONG = "3"
|
||||
case MESSAGE = "4"
|
||||
case UPGRADE = "5"
|
||||
case NOOP = "6"
|
||||
}
|
||||
|
||||
class SocketEngine: NSObject, SRWebSocketDelegate {
|
||||
unowned let client:SocketIOClient
|
||||
private let workQueue = NSOperationQueue()
|
||||
private let emitQueue = dispatch_queue_create(
|
||||
"emitQueue".cStringUsingEncoding(NSUTF8StringEncoding), DISPATCH_QUEUE_SERIAL)
|
||||
private let parseQueue = dispatch_queue_create(
|
||||
"parseQueue".cStringUsingEncoding(NSUTF8StringEncoding), DISPATCH_QUEUE_SERIAL)
|
||||
private let handleQueue = dispatch_queue_create(
|
||||
"handleQueue".cStringUsingEncoding(NSUTF8StringEncoding), DISPATCH_QUEUE_SERIAL)
|
||||
private var forcePolling = false
|
||||
private var pingTimer:NSTimer?
|
||||
private var postWait = [String]()
|
||||
private var _polling = true
|
||||
private var probing = false
|
||||
private var probeWait = PollWaitQueue()
|
||||
private let session:NSURLSession!
|
||||
private var waitingForPoll = false
|
||||
private var waitingForPost = false
|
||||
private var _websocket = false
|
||||
private var websocketConnected = false
|
||||
var connected = false
|
||||
var pingInterval:Int?
|
||||
var polling:Bool {
|
||||
return self._polling
|
||||
}
|
||||
var sid = ""
|
||||
var urlPolling:String?
|
||||
var urlWebSocket:String?
|
||||
var websocket:Bool {
|
||||
return self._websocket
|
||||
}
|
||||
var ws:SRWebSocket?
|
||||
|
||||
init(client:SocketIOClient, forcePolling:Bool = false) {
|
||||
self.client = client
|
||||
self.forcePolling = forcePolling
|
||||
self.session = NSURLSession(configuration: NSURLSessionConfiguration.ephemeralSessionConfiguration(),
|
||||
delegate: nil, delegateQueue: self.workQueue)
|
||||
}
|
||||
|
||||
func close() {
|
||||
self.pingTimer?.invalidate()
|
||||
self.send(PacketType.CLOSE.rawValue)
|
||||
}
|
||||
|
||||
private func createBinaryDataForSend(data:NSData) -> (NSData?, String?) {
|
||||
if self.websocket {
|
||||
var byteArray = [UInt8](count: 1, repeatedValue: 0x0)
|
||||
byteArray[0] = 4
|
||||
var mutData = NSMutableData(bytes: &byteArray, length: 1)
|
||||
mutData.appendData(data)
|
||||
return (mutData, nil)
|
||||
} else {
|
||||
var str = "b4"
|
||||
str += data.base64EncodedStringWithOptions(
|
||||
NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
|
||||
|
||||
return (nil, str)
|
||||
}
|
||||
}
|
||||
|
||||
private func createURLs(params:[String: AnyObject]? = nil) -> (String, String) {
|
||||
var url = "\(self.client.socketURL)/socket.io/?transport="
|
||||
var urlPolling:String
|
||||
var urlWebSocket:String
|
||||
|
||||
if self.client.secure {
|
||||
urlPolling = "https://" + url + "polling"
|
||||
urlWebSocket = "wss://" + url + "websocket"
|
||||
} else {
|
||||
urlPolling = "http://" + url + "polling"
|
||||
urlWebSocket = "ws://" + url + "websocket"
|
||||
}
|
||||
|
||||
if params != nil {
|
||||
for (key, value) in params! {
|
||||
let keyEsc = key.stringByAddingPercentEncodingWithAllowedCharacters(
|
||||
NSCharacterSet.URLHostAllowedCharacterSet())!
|
||||
urlPolling += "&\(keyEsc)="
|
||||
urlWebSocket += "&\(keyEsc)="
|
||||
|
||||
if value is String {
|
||||
let valueEsc = (value as! String).stringByAddingPercentEncodingWithAllowedCharacters(
|
||||
NSCharacterSet.URLHostAllowedCharacterSet())!
|
||||
urlPolling += "\(valueEsc)"
|
||||
urlWebSocket += "\(valueEsc)"
|
||||
} else {
|
||||
urlPolling += "\(value)"
|
||||
urlWebSocket += "\(value)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (urlPolling, urlWebSocket)
|
||||
}
|
||||
|
||||
private func doPoll() {
|
||||
if self.urlPolling == nil || self.websocket || self.waitingForPoll || !self.connected {
|
||||
return
|
||||
}
|
||||
|
||||
let req = NSURLRequest(URL: NSURL(string: self.urlPolling! + "&sid=\(self.sid)")!)
|
||||
self.waitingForPoll = true
|
||||
|
||||
self.session.dataTaskWithRequest(req) {[weak self] data, res, err in
|
||||
if self == nil {
|
||||
return
|
||||
} else if err != nil {
|
||||
if self!.polling {
|
||||
self?.handlePollingFailed(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// println(data)
|
||||
|
||||
if let str = NSString(data: data, encoding: NSUTF8StringEncoding) as? String {
|
||||
// println(str)
|
||||
|
||||
dispatch_async(self!.parseQueue) {[weak self] in
|
||||
self?.parsePollingMessage(str)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
self?.waitingForPoll = false
|
||||
self?.doPoll()
|
||||
}.resume()
|
||||
}
|
||||
|
||||
private func flushProbeWait() {
|
||||
// println("flushing probe wait")
|
||||
dispatch_async(self.emitQueue) {[weak self] in
|
||||
if self == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for waiter in self!.probeWait {
|
||||
waiter()
|
||||
}
|
||||
|
||||
self?.probeWait.removeAll(keepCapacity: false)
|
||||
}
|
||||
}
|
||||
|
||||
private func flushWaitingForPost() {
|
||||
if self.postWait.count == 0 || !self.connected {
|
||||
return
|
||||
} else if self.websocket {
|
||||
self.flushWaitingForPostToWebSocket()
|
||||
return
|
||||
}
|
||||
|
||||
var postStr = ""
|
||||
|
||||
for packet in self.postWait {
|
||||
let len = count(packet)
|
||||
|
||||
postStr += "\(len):\(packet)"
|
||||
}
|
||||
|
||||
self.postWait.removeAll(keepCapacity: false)
|
||||
|
||||
let req = NSMutableURLRequest(URL: NSURL(string: self.urlPolling! + "&sid=\(self.sid)")!)
|
||||
|
||||
req.HTTPMethod = "POST"
|
||||
req.setValue("application/html-text", forHTTPHeaderField: "Content-Type")
|
||||
|
||||
let postData = postStr.dataUsingEncoding(NSUTF8StringEncoding,
|
||||
allowLossyConversion: false)!
|
||||
|
||||
|
||||
req.setValue(String(postData.length), forHTTPHeaderField: "Content-Length")
|
||||
req.HTTPBody = postData
|
||||
|
||||
self.waitingForPost = true
|
||||
|
||||
self.session.dataTaskWithRequest(req) {[weak self] data, res, err in
|
||||
if self == nil {
|
||||
return
|
||||
} else if err != nil && self!.polling {
|
||||
self?.handlePollingFailed(err)
|
||||
return
|
||||
}
|
||||
|
||||
self?.waitingForPost = false
|
||||
dispatch_async(self!.emitQueue) {
|
||||
self?.flushWaitingForPost()
|
||||
self?.doPoll()
|
||||
return
|
||||
}
|
||||
}.resume()
|
||||
}
|
||||
|
||||
// We had packets waiting for send when we upgraded
|
||||
// Send them raw
|
||||
private func flushWaitingForPostToWebSocket() {
|
||||
for msg in self.postWait {
|
||||
self.ws?.send(msg)
|
||||
}
|
||||
|
||||
self.postWait.removeAll(keepCapacity: true)
|
||||
}
|
||||
|
||||
// A poll failed, tell the client about it
|
||||
// We check to see if we were closed by the server first
|
||||
private func handlePollingFailed(reason:NSError?) {
|
||||
if !self.client.reconnecting {
|
||||
self.connected = false
|
||||
self.ws?.close()
|
||||
self.pingTimer?.invalidate()
|
||||
self.waitingForPoll = false
|
||||
self.waitingForPost = false
|
||||
self.client.pollingDidFail(reason)
|
||||
}
|
||||
}
|
||||
|
||||
func open(opts:[String: AnyObject]? = nil) {
|
||||
if self.waitingForPost || self.waitingForPoll || self.websocket || self.connected {
|
||||
assert(false, "We're in a bad state, this shouldn't happen.")
|
||||
}
|
||||
|
||||
let (urlPolling, urlWebSocket) = self.createURLs(params: opts)
|
||||
|
||||
self.urlPolling = urlPolling
|
||||
self.urlWebSocket = urlWebSocket
|
||||
let reqPolling = NSURLRequest(URL: NSURL(string: urlPolling + "&b64=1")!)
|
||||
|
||||
self.session.dataTaskWithRequest(reqPolling) {[weak self] data, res, err in
|
||||
var err2:NSError?
|
||||
if self == nil {
|
||||
return
|
||||
} else if err != nil || data == nil {
|
||||
self?.handlePollingFailed(err)
|
||||
return
|
||||
}
|
||||
|
||||
if let dataString = NSString(data: data, encoding: NSUTF8StringEncoding) {
|
||||
var mutString = RegexMutable(dataString)
|
||||
let parsed:[String]? = mutString["(\\d*):(\\d)(\\{.*\\})?"].groups()
|
||||
|
||||
if parsed == nil || parsed?.count != 4 {
|
||||
return
|
||||
}
|
||||
|
||||
let length = parsed![1]
|
||||
let type = parsed![2]
|
||||
let jsonData = parsed![3].dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
|
||||
|
||||
if type != "0" {
|
||||
NSLog("Error handshaking")
|
||||
return
|
||||
}
|
||||
|
||||
self?.connected = true
|
||||
|
||||
if let json = NSJSONSerialization.JSONObjectWithData(jsonData!,
|
||||
options: NSJSONReadingOptions.AllowFragments, error: &err2) as? NSDictionary {
|
||||
if let sid = json["sid"] as? String {
|
||||
// println(json)
|
||||
self?.sid = sid
|
||||
|
||||
if !self!.forcePolling {
|
||||
self?.ws = SRWebSocket(URL:
|
||||
NSURL(string: urlWebSocket + "&sid=\(self!.sid)")!)
|
||||
self?.ws?.delegate = self
|
||||
self?.ws?.open()
|
||||
}
|
||||
} else {
|
||||
NSLog("Error handshaking")
|
||||
return
|
||||
}
|
||||
|
||||
if let pingInterval = json["pingInterval"] as? Int {
|
||||
self?.pingInterval = pingInterval / 1000
|
||||
}
|
||||
}
|
||||
|
||||
self?.doPoll()
|
||||
self?.startPingTimer()
|
||||
}
|
||||
}.resume()
|
||||
}
|
||||
|
||||
// Translatation of engine.io-parser#decodePayload
|
||||
private func parsePollingMessage(str:String) {
|
||||
if str.length == 1 {
|
||||
return
|
||||
}
|
||||
|
||||
// println(str)
|
||||
|
||||
let strArray = Array(str)
|
||||
var length = ""
|
||||
var n = 0
|
||||
var msg = ""
|
||||
|
||||
func testLength(length:String, inout n:Int) -> Bool {
|
||||
if let num = length.toInt() {
|
||||
n = num
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
for var i = 0, l = str.length; i < l; i = i &+ 1 {
|
||||
let chr = String(strArray[i])
|
||||
|
||||
if chr != ":" {
|
||||
length += chr
|
||||
} else {
|
||||
if length == "" || testLength(length, &n) {
|
||||
self.handlePollingFailed(nil)
|
||||
return
|
||||
}
|
||||
|
||||
msg = String(strArray[i&+1...i&+n])
|
||||
|
||||
if let lengthInt = length.toInt() {
|
||||
if lengthInt != msg.length {
|
||||
println("parsing error")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if msg.length != 0 {
|
||||
// Be sure to capture the value of the msg
|
||||
dispatch_async(self.handleQueue) {[weak self, msg] in
|
||||
fixSwift = msg
|
||||
self?.parseEngineMessage(fixSwift)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
i += n
|
||||
length = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func parseEngineMessage(message:AnyObject?) {
|
||||
// println(message!)
|
||||
if let data = message as? NSData {
|
||||
// Strip off message type
|
||||
self.client.parseSocketMessage(data.subdataWithRange(NSMakeRange(1, data.length - 1)))
|
||||
return
|
||||
}
|
||||
|
||||
var messageString = message as! String
|
||||
var strMessage = RegexMutable(messageString)
|
||||
|
||||
// We should upgrade
|
||||
if strMessage == "3probe" {
|
||||
self.upgradeTransport()
|
||||
return
|
||||
}
|
||||
|
||||
let type = strMessage["^(\\d)"].groups()?[1]
|
||||
|
||||
if type != PacketType.MESSAGE.rawValue {
|
||||
// TODO Handle other packets
|
||||
if messageString.hasPrefix("b4") {
|
||||
// binary in base64 string
|
||||
|
||||
messageString.removeRange(Range<String.Index>(start: messageString.startIndex,
|
||||
end: advance(messageString.startIndex, 2)))
|
||||
|
||||
if let data = NSData(base64EncodedString: messageString,
|
||||
options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters) {
|
||||
// println("sending \(data)")
|
||||
self.client.parseSocketMessage(data)
|
||||
}
|
||||
|
||||
return
|
||||
} else if type == PacketType.NOOP.rawValue {
|
||||
self.doPoll()
|
||||
return
|
||||
}
|
||||
|
||||
if messageString == PacketType.CLOSE.rawValue {
|
||||
// do nothing
|
||||
return
|
||||
}
|
||||
// println("Got something idk what to do with")
|
||||
// println(messageString)
|
||||
}
|
||||
|
||||
// Remove message type
|
||||
messageString.removeAtIndex(messageString.startIndex)
|
||||
// println("sending \(messageString)")
|
||||
|
||||
self.client.parseSocketMessage(messageString)
|
||||
}
|
||||
|
||||
private func probeWebSocket() {
|
||||
if self.websocketConnected {
|
||||
self.sendWebSocketMessage("probe", withType: PacketType.PING)
|
||||
}
|
||||
}
|
||||
|
||||
func send(msg:String, datas:[NSData]? = nil) {
|
||||
let _send = {[weak self] (msg:String, datas:[NSData]?) -> () -> Void in
|
||||
return {
|
||||
if self == nil || !self!.connected {
|
||||
return
|
||||
}
|
||||
|
||||
if self!.websocket {
|
||||
// println("sending ws: \(msg):\(datas)")
|
||||
self?.sendWebSocketMessage(msg, withType: PacketType.MESSAGE, datas: datas)
|
||||
} else {
|
||||
// println("sending poll: \(msg):\(datas)")
|
||||
self?.sendPollMessage(msg, withType: PacketType.MESSAGE, datas: datas)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dispatch_async(self.emitQueue) {[weak self] in
|
||||
if self == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if self!.probing {
|
||||
self?.probeWait.append(_send(msg, datas))
|
||||
} else {
|
||||
_send(msg, datas)()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sendPing() {
|
||||
if self.websocket {
|
||||
self.sendWebSocketMessage("", withType: PacketType.PING)
|
||||
} else {
|
||||
self.sendPollMessage("", withType: PacketType.PING)
|
||||
}
|
||||
}
|
||||
|
||||
private func sendPollMessage(msg:String, withType type:PacketType, datas:[NSData]? = nil) {
|
||||
// println("Sending: poll: \(msg) as type: \(type.rawValue)")
|
||||
let strMsg = "\(type.rawValue)\(msg)"
|
||||
|
||||
self.postWait.append(strMsg)
|
||||
|
||||
if datas != nil {
|
||||
for data in datas! {
|
||||
let (nilData, b64Data) = self.createBinaryDataForSend(data)
|
||||
|
||||
self.postWait.append(b64Data!)
|
||||
}
|
||||
}
|
||||
|
||||
if waitingForPost {
|
||||
self.doPoll()
|
||||
return
|
||||
} else {
|
||||
self.flushWaitingForPost()
|
||||
}
|
||||
}
|
||||
|
||||
private func sendWebSocketMessage(str:String, withType type:PacketType, datas:[NSData]? = nil) {
|
||||
// println("Sending: ws: \(str) as type: \(type.rawValue)")
|
||||
self.ws?.send("\(type.rawValue)\(str)")
|
||||
|
||||
if datas != nil {
|
||||
for data in datas! {
|
||||
let (data, nilString) = self.createBinaryDataForSend(data)
|
||||
if data != nil {
|
||||
self.ws?.send(data!)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Starts the ping timer
|
||||
private func startPingTimer() {
|
||||
if self.pingInterval == nil {
|
||||
return
|
||||
}
|
||||
|
||||
self.pingTimer?.invalidate()
|
||||
dispatch_async(dispatch_get_main_queue()) {
|
||||
self.pingTimer = NSTimer.scheduledTimerWithTimeInterval(NSTimeInterval(self.pingInterval!), target: self,
|
||||
selector: Selector("sendPing"), userInfo: nil, repeats: true)
|
||||
}
|
||||
}
|
||||
|
||||
private func upgradeTransport() {
|
||||
if self.websocketConnected {
|
||||
self.probing = false
|
||||
self._websocket = true
|
||||
self.waitingForPoll = false
|
||||
self._polling = false
|
||||
self.sendWebSocketMessage("", withType: PacketType.UPGRADE)
|
||||
self.flushProbeWait()
|
||||
}
|
||||
}
|
||||
|
||||
// Called when a message is recieved
|
||||
func webSocket(webSocket:SRWebSocket!, didReceiveMessage message:AnyObject?) {
|
||||
// println(message)
|
||||
|
||||
dispatch_async(self.handleQueue) {[weak self] in
|
||||
self?.parseEngineMessage(message)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Called when the socket is opened
|
||||
func webSocketDidOpen(webSocket:SRWebSocket!) {
|
||||
self.websocketConnected = true
|
||||
self.probing = true
|
||||
self.probeWebSocket()
|
||||
}
|
||||
|
||||
// Called when the socket is closed
|
||||
func webSocket(webSocket:SRWebSocket!, didCloseWithCode code:Int, reason:String!, wasClean:Bool) {
|
||||
self.websocketConnected = false
|
||||
self.probing = false
|
||||
|
||||
if self.websocket {
|
||||
self.pingTimer?.invalidate()
|
||||
self.connected = false
|
||||
self._websocket = false
|
||||
self._polling = true
|
||||
self.client.webSocketDidCloseWithCode(code, reason: reason, wasClean: wasClean)
|
||||
} else {
|
||||
self.flushProbeWait()
|
||||
}
|
||||
}
|
||||
|
||||
// Called when an error occurs.
|
||||
func webSocket(webSocket:SRWebSocket!, didFailWithError error:NSError!) {
|
||||
self.websocketConnected = false
|
||||
self._polling = true
|
||||
self.probing = false
|
||||
|
||||
if self.websocket {
|
||||
self.pingTimer?.invalidate()
|
||||
self.connected = false
|
||||
self.client.webSocketDidFailWithError(error)
|
||||
} else {
|
||||
self.flushProbeWait()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -74,29 +74,29 @@ class SocketEvent {
|
||||
if !hasBinary {
|
||||
if nsp == nil {
|
||||
if ack == nil {
|
||||
message = "42[\"\(event)\""
|
||||
message = "2[\"\(event)\""
|
||||
} else {
|
||||
message = "42\(ack!)[\"\(event)\""
|
||||
message = "2\(ack!)[\"\(event)\""
|
||||
}
|
||||
} else {
|
||||
if ack == nil {
|
||||
message = "42/\(nsp!),[\"\(event)\""
|
||||
message = "2/\(nsp!),[\"\(event)\""
|
||||
} else {
|
||||
message = "42/\(nsp!),\(ack!)[\"\(event)\""
|
||||
message = "2/\(nsp!),\(ack!)[\"\(event)\""
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if nsp == nil {
|
||||
if ack == nil {
|
||||
message = "45\(datas)-[\"\(event)\""
|
||||
message = "5\(datas)-[\"\(event)\""
|
||||
} else {
|
||||
message = "45\(datas)-\(ack!)[\"\(event)\""
|
||||
message = "5\(datas)-\(ack!)[\"\(event)\""
|
||||
}
|
||||
} else {
|
||||
if ack == nil {
|
||||
message = "45\(datas)-/\(nsp!),[\"\(event)\""
|
||||
message = "5\(datas)-/\(nsp!),[\"\(event)\""
|
||||
} else {
|
||||
message = "45\(datas)-/\(nsp!),\(ack!)[\"\(event)\""
|
||||
message = "5\(datas)-/\(nsp!),\(ack!)[\"\(event)\""
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -110,15 +110,15 @@ class SocketEvent {
|
||||
|
||||
if ackType == 3 {
|
||||
if nsp == "/" {
|
||||
msg = "43\(ack)["
|
||||
msg = "3\(ack)["
|
||||
} else {
|
||||
msg = "43/\(nsp),\(ack)["
|
||||
msg = "3/\(nsp),\(ack)["
|
||||
}
|
||||
} else {
|
||||
if nsp == "/" {
|
||||
msg = "46\(binary)-\(ack)["
|
||||
msg = "6\(binary)-\(ack)["
|
||||
} else {
|
||||
msg = "46\(binary)-/\(nsp),\(ack)["
|
||||
msg = "6\(binary)-/\(nsp),\(ack)["
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
// THE SOFTWARE.
|
||||
|
||||
typealias NormalCallback = (NSArray?, AckEmitter?) -> Void
|
||||
typealias AnyHandler = (event:String, items:AnyObject?)
|
||||
typealias AckEmitter = (AnyObject...) -> Void
|
||||
|
||||
private func emitAckCallback(socket:SocketIOClient, num:Int, type:Int) -> AckEmitter {
|
||||
@ -44,6 +45,9 @@ class SocketEventHandler {
|
||||
|
||||
func executeCallback(_ items:NSArray? = nil, withAck ack:Int? = nil, withAckType type:Int? = nil,
|
||||
withSocket socket:SocketIOClient? = nil) {
|
||||
callback?(items, ack != nil ? emitAckCallback(socket!, ack!, type!) : nil)
|
||||
dispatch_async(dispatch_get_main_queue()) {[weak self] in
|
||||
self?.callback?(items, ack != nil ? emitAckCallback(socket!, ack!, type!) : nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -24,7 +24,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
class SocketIOClient {
|
||||
let socketURL:NSMutableString!
|
||||
let ackQueue = dispatch_queue_create("ackQueue".cStringUsingEncoding(NSUTF8StringEncoding),
|
||||
DISPATCH_QUEUE_SERIAL)
|
||||
@ -32,30 +32,36 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
DISPATCH_QUEUE_SERIAL)
|
||||
let emitQueue = dispatch_queue_create("emitQueue".cStringUsingEncoding(NSUTF8StringEncoding),
|
||||
DISPATCH_QUEUE_SERIAL)
|
||||
private lazy var params:[String: AnyObject] = [String: AnyObject]()
|
||||
let reconnectAttempts:Int!
|
||||
private lazy var params = [String: AnyObject]()
|
||||
private var ackHandlers = [SocketAckHandler]()
|
||||
private var anyHandler:((AnyHandler) -> Void)?
|
||||
private var currentAck = -1
|
||||
private var currentReconnectAttempt = 0
|
||||
private var forcePolling = false
|
||||
private var handlers = [SocketEventHandler]()
|
||||
private var lastSocketMessage:SocketEvent?
|
||||
private var waitingData = [SocketEvent]()
|
||||
private var paramConnect = false
|
||||
private var pingTimer:NSTimer!
|
||||
private var secure = false
|
||||
private var _secure = false
|
||||
private var reconnectTimer:NSTimer?
|
||||
var closed = false
|
||||
var connected = false
|
||||
var connecting = false
|
||||
var io:SRWebSocket?
|
||||
var engine:SocketEngine?
|
||||
var nsp:String?
|
||||
var reconnects = true
|
||||
var reconnecting = false
|
||||
var reconnectAttempts = -1
|
||||
var reconnectWait = 10
|
||||
var secure:Bool {
|
||||
return self._secure
|
||||
}
|
||||
var sid:String?
|
||||
|
||||
init(socketURL:String, opts:[String: AnyObject]? = nil) {
|
||||
var mutURL = RegexMutable(socketURL)
|
||||
|
||||
if mutURL["https://"].matches().count != 0 {
|
||||
self.secure = true
|
||||
self._secure = true
|
||||
}
|
||||
|
||||
mutURL = mutURL["http://"] ~= ""
|
||||
@ -71,6 +77,8 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
|
||||
if let reconnectAttempts = opts!["reconnectAttempts"] as? Int {
|
||||
self.reconnectAttempts = reconnectAttempts
|
||||
} else {
|
||||
self.reconnectAttempts = -1
|
||||
}
|
||||
|
||||
if let reconnectWait = opts!["reconnectWait"] as? Int {
|
||||
@ -80,75 +88,68 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
if let nsp = opts!["nsp"] as? String {
|
||||
self.nsp = nsp
|
||||
}
|
||||
|
||||
if let polling = opts!["forcePolling"] as? Bool {
|
||||
self.forcePolling = polling
|
||||
}
|
||||
} else {
|
||||
self.reconnectAttempts = -1
|
||||
}
|
||||
|
||||
self.engine = SocketEngine(client: self, forcePolling: self.forcePolling)
|
||||
}
|
||||
|
||||
// Closes the socket
|
||||
func close() {
|
||||
self.pingTimer?.invalidate()
|
||||
self.closed = true
|
||||
self.connecting = false
|
||||
self.connected = false
|
||||
self.io?.send("41")
|
||||
self.io?.close()
|
||||
self.reconnecting = false
|
||||
self.engine?.close()
|
||||
}
|
||||
|
||||
// Connects to the server
|
||||
func connect() {
|
||||
self.connectWithURL(self.createConnectURL())
|
||||
if self.closed {
|
||||
println("Warning! This socket was previously closed. This might be dangerous!")
|
||||
self.closed = false
|
||||
}
|
||||
|
||||
self.engine?.open()
|
||||
}
|
||||
|
||||
// Connect to the server using params
|
||||
func connectWithParams(params:[String: AnyObject]) {
|
||||
if self.closed {
|
||||
println("Warning! This socket was previously closed. This might be dangerous!")
|
||||
self.closed = false
|
||||
}
|
||||
|
||||
self.params = params
|
||||
self.paramConnect = true
|
||||
var endpoint = self.createConnectURL()
|
||||
|
||||
for (key, value) in params {
|
||||
let keyEsc = key.stringByAddingPercentEncodingWithAllowedCharacters(
|
||||
NSCharacterSet.URLHostAllowedCharacterSet())!
|
||||
endpoint += "&\(keyEsc)="
|
||||
|
||||
if value is String {
|
||||
let valueEsc = (value as! String).stringByAddingPercentEncodingWithAllowedCharacters(
|
||||
NSCharacterSet.URLHostAllowedCharacterSet())!
|
||||
endpoint += "\(valueEsc)"
|
||||
} else {
|
||||
endpoint += "\(value)"
|
||||
}
|
||||
}
|
||||
|
||||
self.connectWithURL(endpoint)
|
||||
self.engine?.open(opts: params)
|
||||
}
|
||||
|
||||
private func connectWithURL(url:String) {
|
||||
if self.closed {
|
||||
println("Warning: This socket was previvously closed. Reopening could be dangerous. Be careful.")
|
||||
}
|
||||
|
||||
self.connecting = true
|
||||
func didConnect() {
|
||||
self.closed = false
|
||||
|
||||
self.io = SRWebSocket(URL: NSURL(string: url))
|
||||
self.io?.delegate = self
|
||||
self.io?.open()
|
||||
self.connected = true
|
||||
self.connecting = false
|
||||
self.reconnecting = false
|
||||
self.currentReconnectAttempt = 0
|
||||
self.reconnectTimer?.invalidate()
|
||||
self.reconnectTimer = nil
|
||||
self.handleEvent("connect", data: nil, isInternalMessage: false)
|
||||
}
|
||||
|
||||
// Creates a binary message, ready for sending
|
||||
private class func createBinaryDataForSend(data:NSData) -> NSData {
|
||||
var byteArray = [UInt8](count: 1, repeatedValue: 0x0)
|
||||
byteArray[0] = 4
|
||||
var mutData = NSMutableData(bytes: &byteArray, length: 1)
|
||||
mutData.appendData(data)
|
||||
return mutData
|
||||
}
|
||||
|
||||
private func createConnectURL() -> String {
|
||||
if self.secure {
|
||||
return "wss://\(self.socketURL)/socket.io/?transport=websocket"
|
||||
} else {
|
||||
return "ws://\(self.socketURL)/socket.io/?transport=websocket"
|
||||
}
|
||||
// Server wants us to die
|
||||
func didForceClose() {
|
||||
self.closed = true
|
||||
self.connected = false
|
||||
self.reconnects = false
|
||||
self.connecting = false
|
||||
self.reconnecting = false
|
||||
self.handleEvent("disconnect", data: "closed", isInternalMessage: true)
|
||||
}
|
||||
|
||||
// Sends a message with multiple args
|
||||
@ -160,11 +161,8 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
}
|
||||
|
||||
dispatch_async(self.emitQueue) {[weak self] in
|
||||
if self == nil {
|
||||
return
|
||||
}
|
||||
|
||||
self?._emit(event, args)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,11 +176,8 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
self.ackHandlers.append(ackHandler)
|
||||
|
||||
dispatch_async(self.emitQueue) {[weak self] in
|
||||
if self == nil {
|
||||
return
|
||||
}
|
||||
|
||||
self?._emit(event, args, ack: true)
|
||||
return
|
||||
}
|
||||
|
||||
return ackHandler
|
||||
@ -207,10 +202,7 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
hasBinary: true, withDatas: emitDatas.count, toNamespace: self.nsp, wantsAck: self.currentAck)
|
||||
}
|
||||
|
||||
self.io?.send(str)
|
||||
for data in emitDatas {
|
||||
self.io?.send(data)
|
||||
}
|
||||
self.engine?.send(str, datas: emitDatas)
|
||||
} else {
|
||||
if !ack {
|
||||
str = SocketEvent.createMessageForEvent(event, withArgs: items, hasBinary: false,
|
||||
@ -220,7 +212,7 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
withDatas: 0, toNamespace: self.nsp, wantsAck: self.currentAck)
|
||||
}
|
||||
|
||||
self.io?.send(str)
|
||||
self.engine?.send(str)
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,7 +235,7 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
withAckType: 3, withNsp: self!.nsp!)
|
||||
}
|
||||
|
||||
self?.io?.send(str)
|
||||
self?.engine?.send(str)
|
||||
} else {
|
||||
if self?.nsp == nil {
|
||||
str = SocketEvent.createAck(ack, withArgs: items,
|
||||
@ -253,10 +245,7 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
withAckType: 6, withNsp: self!.nsp!, withBinary: emitDatas.count)
|
||||
}
|
||||
|
||||
self?.io?.send(str)
|
||||
for data in emitDatas {
|
||||
self?.io?.send(data)
|
||||
}
|
||||
self?.engine?.send(str, datas: emitDatas)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -267,7 +256,14 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
if handler.ackNum != ack {
|
||||
return true
|
||||
} else {
|
||||
handler.callback?(data)
|
||||
if data is NSArray {
|
||||
handler.callback?(data as? NSArray)
|
||||
} else if data != nil {
|
||||
handler.callback?([data!])
|
||||
} else {
|
||||
handler.callback?(nil)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -277,45 +273,48 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
func handleEvent(event:String, data:AnyObject?, isInternalMessage:Bool = false,
|
||||
wantsAck ack:Int? = nil, withAckType ackType:Int = 3) {
|
||||
// println("Should do event: \(event) with data: \(data)")
|
||||
dispatch_async(dispatch_get_main_queue()) {
|
||||
if !self.connected && !isInternalMessage {
|
||||
return
|
||||
}
|
||||
|
||||
for handler in self.handlers {
|
||||
if handler.event == event {
|
||||
if data is NSArray {
|
||||
if ack != nil {
|
||||
handler.executeCallback(data as? NSArray, withAck: ack!,
|
||||
withAckType: ackType, withSocket: self)
|
||||
} else {
|
||||
handler.executeCallback(data as? NSArray)
|
||||
}
|
||||
if !self.connected && !isInternalMessage {
|
||||
return
|
||||
}
|
||||
|
||||
dispatch_async(dispatch_get_main_queue()) {[weak self] in
|
||||
self?.anyHandler?((event, data))
|
||||
return
|
||||
}
|
||||
for handler in self.handlers {
|
||||
if handler.event == event {
|
||||
if data is NSArray {
|
||||
if ack != nil {
|
||||
handler.executeCallback(data as? NSArray, withAck: ack!,
|
||||
withAckType: ackType, withSocket: self)
|
||||
} else {
|
||||
|
||||
// Trying to do a ternary expression in the executeCallback method
|
||||
// seemed to crash Swift
|
||||
var dataArr:NSArray? = nil
|
||||
|
||||
if let data:AnyObject = data {
|
||||
dataArr = [data]
|
||||
}
|
||||
|
||||
if ack != nil {
|
||||
handler.executeCallback(dataArr, withAck: ack!,
|
||||
withAckType: ackType, withSocket: self)
|
||||
} else {
|
||||
handler.executeCallback(dataArr)
|
||||
}
|
||||
handler.executeCallback(data as? NSArray)
|
||||
}
|
||||
} else {
|
||||
|
||||
// Trying to do a ternary expression in the executeCallback method
|
||||
// seemed to crash Swift
|
||||
var dataArr:NSArray? = nil
|
||||
|
||||
if let data:AnyObject = data {
|
||||
dataArr = [data]
|
||||
}
|
||||
|
||||
if ack != nil {
|
||||
handler.executeCallback(dataArr, withAck: ack!,
|
||||
withAckType: ackType, withSocket: self)
|
||||
} else {
|
||||
handler.executeCallback(dataArr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func joinNamespace() {
|
||||
// Should be removed and moved to SocketEngine
|
||||
func joinNamespace() {
|
||||
if self.nsp != nil {
|
||||
self.io?.send("40/\(self.nsp!)")
|
||||
self.engine?.send("0/\(self.nsp!)")
|
||||
}
|
||||
}
|
||||
|
||||
@ -325,35 +324,38 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
self.handlers.append(handler)
|
||||
}
|
||||
|
||||
// Adds a handler for any event
|
||||
func onAny(handler:(AnyHandler) -> Void) {
|
||||
self.anyHandler = handler
|
||||
}
|
||||
|
||||
// Opens the connection to the socket
|
||||
func open() {
|
||||
self.connect()
|
||||
}
|
||||
|
||||
// Parse an NSArray looking for binary data
|
||||
private class func parseArray(arr:NSArray, var placeholders:Int) -> (NSArray, Bool, [NSData]) {
|
||||
private class func parseArray(arr:NSArray, var currentPlaceholder:Int) -> (NSArray, Bool, [NSData]) {
|
||||
var replacementArr = [AnyObject](count: arr.count, repeatedValue: 1)
|
||||
var hasBinary = false
|
||||
var arrayDatas = [NSData]()
|
||||
|
||||
if placeholders == -1 {
|
||||
placeholders = 0
|
||||
}
|
||||
|
||||
for g in 0..<arr.count {
|
||||
if arr[g] is NSData {
|
||||
hasBinary = true
|
||||
let sendData = self.createBinaryDataForSend(arr[g] as! NSData)
|
||||
currentPlaceholder++
|
||||
let sendData = arr[g] as! NSData
|
||||
|
||||
arrayDatas.append(sendData)
|
||||
replacementArr[g] = ["_placeholder": true,
|
||||
"num": placeholders++]
|
||||
"num": currentPlaceholder]
|
||||
} else if let dict = arr[g] as? NSDictionary {
|
||||
let (nestDict, hadBinary, dictArrs) = self.parseNSDictionary(dict, placeholders: placeholders)
|
||||
let (nestDict, hadBinary, dictArrs) = self.parseNSDictionary(dict,
|
||||
currentPlaceholder: currentPlaceholder)
|
||||
|
||||
if hadBinary {
|
||||
hasBinary = true
|
||||
placeholders += dictArrs.count
|
||||
currentPlaceholder += dictArrs.count
|
||||
replacementArr[g] = nestDict
|
||||
arrayDatas.extend(dictArrs)
|
||||
} else {
|
||||
@ -361,11 +363,12 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
}
|
||||
} else if let nestArr = arr[g] as? NSArray {
|
||||
// Recursive
|
||||
let (nested, hadBinary, nestDatas) = self.parseArray(nestArr, placeholders: placeholders)
|
||||
let (nested, hadBinary, nestDatas) = self.parseArray(nestArr,
|
||||
currentPlaceholder: currentPlaceholder)
|
||||
|
||||
if hadBinary {
|
||||
hasBinary = true
|
||||
placeholders += nestDatas.count
|
||||
currentPlaceholder += nestDatas.count
|
||||
replacementArr[g] = nested
|
||||
arrayDatas.extend(nestDatas)
|
||||
} else {
|
||||
@ -400,7 +403,7 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
|
||||
private class func parseEmitArgs(args:[AnyObject]) -> ([AnyObject], Bool, [NSData]) {
|
||||
var items = [AnyObject](count: args.count, repeatedValue: 1)
|
||||
var numberOfPlaceholders = -1
|
||||
var currentPlaceholder = -1
|
||||
var hasBinary = false
|
||||
var emitDatas = [NSData]()
|
||||
|
||||
@ -408,9 +411,9 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
if let dict = args[i] as? NSDictionary {
|
||||
// Check for binary data
|
||||
let (newDict, hadBinary, binaryDatas) = SocketIOClient.parseNSDictionary(dict,
|
||||
placeholders: numberOfPlaceholders)
|
||||
currentPlaceholder: currentPlaceholder)
|
||||
if hadBinary {
|
||||
numberOfPlaceholders = binaryDatas.count
|
||||
currentPlaceholder += binaryDatas.count
|
||||
|
||||
emitDatas.extend(binaryDatas)
|
||||
hasBinary = true
|
||||
@ -421,11 +424,11 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
} else if let arr = args[i] as? NSArray {
|
||||
// arg is array, check for binary
|
||||
let (replace, hadData, newDatas) = SocketIOClient.parseArray(arr,
|
||||
placeholders: numberOfPlaceholders)
|
||||
currentPlaceholder: currentPlaceholder)
|
||||
|
||||
if hadData {
|
||||
hasBinary = true
|
||||
numberOfPlaceholders += emitDatas.count
|
||||
currentPlaceholder += newDatas.count
|
||||
|
||||
for data in newDatas {
|
||||
emitDatas.append(data)
|
||||
@ -438,11 +441,10 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
} else if let binaryData = args[i] as? NSData {
|
||||
// args is just binary
|
||||
hasBinary = true
|
||||
let sendData = SocketIOClient.createBinaryDataForSend(binaryData)
|
||||
|
||||
numberOfPlaceholders++
|
||||
items[i] = ["_placeholder": true, "num": numberOfPlaceholders]
|
||||
emitDatas.append(sendData)
|
||||
currentPlaceholder++
|
||||
items[i] = ["_placeholder": true, "num": currentPlaceholder]
|
||||
emitDatas.append(binaryData)
|
||||
} else {
|
||||
items[i] = args[i]
|
||||
}
|
||||
@ -452,39 +454,36 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
}
|
||||
|
||||
// Parses a NSDictionary, looking for NSData objects
|
||||
private class func parseNSDictionary(dict:NSDictionary, var placeholders:Int) -> (NSDictionary, Bool, [NSData]) {
|
||||
private class func parseNSDictionary(dict:NSDictionary, var currentPlaceholder:Int) -> (NSDictionary, Bool, [NSData]) {
|
||||
var returnDict = NSMutableDictionary()
|
||||
var hasBinary = false
|
||||
if placeholders == -1 {
|
||||
placeholders = 0
|
||||
}
|
||||
var returnDatas = [NSData]()
|
||||
|
||||
for (key, value) in dict {
|
||||
if let binaryData = value as? NSData {
|
||||
currentPlaceholder++
|
||||
hasBinary = true
|
||||
let sendData = self.createBinaryDataForSend(binaryData)
|
||||
returnDatas.append(sendData)
|
||||
returnDict[key as! String] = ["_placeholder": true, "num": placeholders++]
|
||||
returnDatas.append(binaryData)
|
||||
returnDict[key as! String] = ["_placeholder": true, "num": currentPlaceholder++]
|
||||
} else if let arr = value as? NSArray {
|
||||
let (replace, hadBinary, arrDatas) = self.parseArray(arr, placeholders: placeholders)
|
||||
let (replace, hadBinary, arrDatas) = self.parseArray(arr, currentPlaceholder: currentPlaceholder)
|
||||
|
||||
if hadBinary {
|
||||
hasBinary = true
|
||||
returnDict[key as! String] = replace
|
||||
placeholders += arrDatas.count
|
||||
currentPlaceholder += arrDatas.count
|
||||
returnDatas.extend(arrDatas)
|
||||
} else {
|
||||
returnDict[key as! String] = arr
|
||||
}
|
||||
} else if let dict = value as? NSDictionary {
|
||||
// Recursive
|
||||
let (nestDict, hadBinary, nestDatas) = self.parseNSDictionary(dict, placeholders: placeholders)
|
||||
let (nestDict, hadBinary, nestDatas) = self.parseNSDictionary(dict, currentPlaceholder: currentPlaceholder)
|
||||
|
||||
if hadBinary {
|
||||
hasBinary = true
|
||||
returnDict[key as! String] = nestDict
|
||||
placeholders += nestDatas.count
|
||||
currentPlaceholder += nestDatas.count
|
||||
returnDatas.extend(nestDatas)
|
||||
} else {
|
||||
returnDict[key as! String] = dict
|
||||
@ -498,7 +497,7 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
}
|
||||
|
||||
// Parses messages recieved
|
||||
private func parseSocketMessage(message:AnyObject?) {
|
||||
func parseSocketMessage(message:AnyObject?) {
|
||||
if message == nil {
|
||||
return
|
||||
}
|
||||
@ -508,40 +507,33 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
if let stringMessage = message as? String {
|
||||
// Check for successful namepsace connect
|
||||
if self.nsp != nil {
|
||||
if stringMessage == "40/\(self.nsp!)" {
|
||||
self.handleEvent("connect", data: nil)
|
||||
if stringMessage == "0/\(self.nsp!)" {
|
||||
self.didConnect()
|
||||
return
|
||||
}
|
||||
}
|
||||
/**
|
||||
Begin check for socket info frame
|
||||
**/
|
||||
var mutMessage = RegexMutable(stringMessage)
|
||||
var setup:String!
|
||||
let messageData = mutMessage["(\\d*)(\\{.*\\})?"].groups()
|
||||
|
||||
if messageData != nil && messageData[1] == "0" {
|
||||
setup = messageData[2]
|
||||
let data = setup.dataUsingEncoding(NSUTF8StringEncoding)!
|
||||
var jsonError:NSError?
|
||||
|
||||
if let json:AnyObject? = NSJSONSerialization.JSONObjectWithData(data,
|
||||
options: nil, error: &jsonError) {
|
||||
self.sid = json!["sid"] as? String
|
||||
self.startPingTimer(interval: (json!["pingInterval"] as! Int) / 1000)
|
||||
return
|
||||
if stringMessage == "0" {
|
||||
if self.nsp != nil {
|
||||
// Join namespace
|
||||
self.joinNamespace()
|
||||
return
|
||||
} else {
|
||||
// Don't handle as internal because something crazy could happen where
|
||||
// we disconnect before it's handled
|
||||
self.didConnect()
|
||||
return
|
||||
}
|
||||
}
|
||||
/**
|
||||
End check for socket info frame
|
||||
**/
|
||||
|
||||
var mutMessage = RegexMutable(stringMessage)
|
||||
|
||||
/**
|
||||
Begin check for message
|
||||
**/
|
||||
let messageGroups = mutMessage["(\\d*)\\/?(\\w*)?,?(\\d*)?(\\[.*\\])?"].groups()
|
||||
|
||||
if messageGroups[1].hasPrefix("42") {
|
||||
if messageGroups[1].hasPrefix("2") {
|
||||
var mesNum = messageGroups[1]
|
||||
var ackNum:String
|
||||
var namespace:String?
|
||||
@ -550,7 +542,7 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
if messageGroups[3] != "" {
|
||||
ackNum = messageGroups[3]
|
||||
} else {
|
||||
let range = Range<String.Index>(start: mesNum.startIndex, end: advance(mesNum.startIndex, 2))
|
||||
let range = Range<String.Index>(start: mesNum.startIndex, end: advance(mesNum.startIndex, 1))
|
||||
mesNum.replaceRange(range, with: "")
|
||||
ackNum = mesNum
|
||||
}
|
||||
@ -615,7 +607,7 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
}
|
||||
return
|
||||
}
|
||||
} else if messageGroups[1].hasPrefix("43") {
|
||||
} else if messageGroups[1].hasPrefix("3") {
|
||||
let arr = Array(messageGroups[1])
|
||||
var ackNum:String
|
||||
let nsp = messageGroups[2]
|
||||
@ -625,7 +617,7 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
}
|
||||
|
||||
if nsp == "" {
|
||||
ackNum = String(arr[2...arr.count-1])
|
||||
ackNum = String(arr[1...arr.count-1])
|
||||
} else {
|
||||
ackNum = messageGroups[3]
|
||||
}
|
||||
@ -645,7 +637,7 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
|
||||
// Message is binary
|
||||
if let binary = message as? NSData {
|
||||
if self.lastSocketMessage == nil {
|
||||
if self.waitingData.isEmpty {
|
||||
return
|
||||
}
|
||||
|
||||
@ -669,7 +661,7 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
return
|
||||
}
|
||||
|
||||
if binaryGroup[1].hasPrefix("45") {
|
||||
if binaryGroup[1].hasPrefix("5") {
|
||||
// println(binaryGroup)
|
||||
var ackNum:String
|
||||
var event:String
|
||||
@ -687,12 +679,11 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
ackNum = ""
|
||||
}
|
||||
|
||||
numberOfPlaceholders = (messageType["45"] ~= "") as String
|
||||
numberOfPlaceholders = (messageType["5"] ~= "") as String
|
||||
event = (RegexMutable(binaryGroup[4])["\""] ~= "") as String
|
||||
mutMessageObject = RegexMutable(binaryGroup[5])
|
||||
|
||||
if namespace == "" && self.nsp != nil {
|
||||
self.lastSocketMessage = nil
|
||||
return
|
||||
}
|
||||
|
||||
@ -709,10 +700,10 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
placeholders: numberOfPlaceholders.toInt()!, ackNum: ackNum.toInt())
|
||||
}
|
||||
|
||||
self.lastSocketMessage = mes
|
||||
} else if binaryGroup[1].hasPrefix("46") {
|
||||
self.waitingData.append(mes)
|
||||
} else if binaryGroup[1].hasPrefix("6") {
|
||||
let messageType = RegexMutable(binaryGroup[1])
|
||||
let numberOfPlaceholders = (messageType["46"] ~= "") as String
|
||||
let numberOfPlaceholders = (messageType["6"] ~= "") as String
|
||||
var ackNum:String
|
||||
var nsp:String
|
||||
|
||||
@ -731,8 +722,10 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
let placeholdersRemoved = mutMessageObject["(\\{\"_placeholder\":true,\"num\":(\\d*)\\})"]
|
||||
~= "\"~~$2\""
|
||||
|
||||
self.lastSocketMessage = SocketEvent(event: "", args: placeholdersRemoved,
|
||||
let event = SocketEvent(event: "", args: placeholdersRemoved,
|
||||
placeholders: numberOfPlaceholders.toInt()!, ackNum: ackNum.toInt(), justAck: true)
|
||||
|
||||
self.waitingData.append(event)
|
||||
}
|
||||
/**
|
||||
End check for binary placeholders
|
||||
@ -742,37 +735,42 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
|
||||
// Handles binary data
|
||||
private func parseBinaryData(data:NSData) {
|
||||
let shouldExecute = self.lastSocketMessage?.addData(data)
|
||||
let shouldExecute = self.waitingData[0].addData(data)
|
||||
|
||||
if shouldExecute != nil && shouldExecute! {
|
||||
var event = self.lastSocketMessage!.event
|
||||
var parsedArgs:AnyObject? = SocketIOClient.parseData(self.lastSocketMessage!.args as? String)
|
||||
if shouldExecute {
|
||||
let socketEvent = self.waitingData.removeAtIndex(0)
|
||||
var event = socketEvent.event
|
||||
var parsedArgs:AnyObject? = SocketIOClient.parseData(socketEvent.args as? String)
|
||||
|
||||
if let args:AnyObject = parsedArgs {
|
||||
let filledInArgs:AnyObject = self.lastSocketMessage!.fillInPlaceholders(args)
|
||||
let filledInArgs:AnyObject = socketEvent.fillInPlaceholders(args)
|
||||
|
||||
if self.lastSocketMessage!.justAck! {
|
||||
self.handleAck(self.lastSocketMessage!.ack!, data: filledInArgs)
|
||||
if socketEvent.justAck! {
|
||||
// Should handle ack
|
||||
self.handleAck(socketEvent.ack!, data: filledInArgs)
|
||||
return
|
||||
}
|
||||
|
||||
if self.lastSocketMessage!.ack != nil {
|
||||
// Should do event
|
||||
if socketEvent.ack != nil {
|
||||
self.handleEvent(event, data: filledInArgs, isInternalMessage: false,
|
||||
wantsAck: self.lastSocketMessage!.ack!, withAckType: 6)
|
||||
wantsAck: socketEvent.ack!, withAckType: 6)
|
||||
} else {
|
||||
self.handleEvent(event, data: filledInArgs)
|
||||
}
|
||||
} else {
|
||||
let filledInArgs:AnyObject = self.lastSocketMessage!.fillInPlaceholders()
|
||||
let filledInArgs:AnyObject = socketEvent.fillInPlaceholders()
|
||||
|
||||
if self.lastSocketMessage!.justAck! {
|
||||
self.handleAck(self.lastSocketMessage!.ack!, data: filledInArgs)
|
||||
// Should handle ack
|
||||
if socketEvent.justAck! {
|
||||
self.handleAck(socketEvent.ack!, data: filledInArgs)
|
||||
return
|
||||
}
|
||||
|
||||
if self.lastSocketMessage!.ack != nil {
|
||||
// Should handle ack
|
||||
if socketEvent.ack != nil {
|
||||
self.handleEvent(event, data: filledInArgs, isInternalMessage: false,
|
||||
wantsAck: self.lastSocketMessage!.ack!, withAckType: 6)
|
||||
wantsAck: socketEvent.ack!, withAckType: 6)
|
||||
} else {
|
||||
self.handleEvent(event, data: filledInArgs)
|
||||
}
|
||||
@ -780,27 +778,19 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func sendPing() {
|
||||
if self.connected {
|
||||
self.io?.send("2")
|
||||
}
|
||||
}
|
||||
|
||||
// Starts the ping timer
|
||||
private func startPingTimer(#interval:Int) {
|
||||
dispatch_async(dispatch_get_main_queue()) {
|
||||
self.pingTimer = NSTimer.scheduledTimerWithTimeInterval(NSTimeInterval(interval), target: self,
|
||||
selector: Selector("sendPing"), userInfo: nil, repeats: true)
|
||||
// Something happened while polling
|
||||
func pollingDidFail(err:NSError?) {
|
||||
if !self.reconnecting {
|
||||
self.connected = false
|
||||
self.handleEvent("reconnect", data: err?.localizedDescription, isInternalMessage: true)
|
||||
self.tryReconnect()
|
||||
}
|
||||
}
|
||||
|
||||
// We lost connection and should attempt to reestablish
|
||||
private func tryReconnect(var #triesLeft:Int) {
|
||||
if triesLeft != -1 && triesLeft <= 0 {
|
||||
self.connecting = false
|
||||
self.reconnects = false
|
||||
self.reconnecting = false
|
||||
self.handleEvent("disconnect", data: "Failed to reconnect", isInternalMessage: true)
|
||||
@objc func tryReconnect() {
|
||||
if self.reconnectAttempts != -1 && self.currentReconnectAttempt + 1 > self.reconnectAttempts {
|
||||
self.didForceClose()
|
||||
return
|
||||
} else if self.connected {
|
||||
self.connecting = false
|
||||
@ -808,26 +798,25 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
return
|
||||
}
|
||||
|
||||
// println("Trying to reconnect #\(reconnectAttempts - triesLeft)")
|
||||
self.handleEvent("reconnectAttempt", data: triesLeft, isInternalMessage: true)
|
||||
|
||||
let waitTime = UInt64(self.reconnectWait) * NSEC_PER_SEC
|
||||
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(waitTime))
|
||||
|
||||
// Wait reconnectWait seconds and then check if connected. Repeat if not
|
||||
dispatch_after(time, dispatch_get_main_queue()) {[weak self] in
|
||||
if self == nil || self!.connected || self!.closed {
|
||||
if self.reconnectTimer == nil {
|
||||
self.reconnecting = true
|
||||
dispatch_async(dispatch_get_main_queue()) {[weak self] in
|
||||
if self == nil {
|
||||
return
|
||||
}
|
||||
|
||||
self?.reconnectTimer = NSTimer.scheduledTimerWithTimeInterval(Double(self!.reconnectWait),
|
||||
target: self!, selector: "tryReconnect", userInfo: nil, repeats: true)
|
||||
return
|
||||
}
|
||||
|
||||
if triesLeft != -1 {
|
||||
triesLeft = triesLeft - 1
|
||||
}
|
||||
|
||||
self!.tryReconnect(triesLeft: triesLeft)
|
||||
return
|
||||
}
|
||||
self.reconnecting = true
|
||||
|
||||
self.handleEvent("reconnectAttempt", data: self.reconnectAttempts - self.currentReconnectAttempt,
|
||||
isInternalMessage: true)
|
||||
|
||||
self.currentReconnectAttempt++
|
||||
if self.paramConnect {
|
||||
self.connectWithParams(self.params)
|
||||
} else {
|
||||
@ -835,60 +824,28 @@ class SocketIOClient: NSObject, SRWebSocketDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
// Called when a message is recieved
|
||||
func webSocket(webSocket:SRWebSocket!, didReceiveMessage message:AnyObject?) {
|
||||
dispatch_async(self.handleQueue) {[weak self] in
|
||||
if self == nil {
|
||||
return
|
||||
}
|
||||
|
||||
self?.parseSocketMessage(message)
|
||||
}
|
||||
}
|
||||
|
||||
// Called when the socket is opened
|
||||
func webSocketDidOpen(webSocket:SRWebSocket!) {
|
||||
self.closed = false
|
||||
self.connecting = false
|
||||
self.reconnecting = false
|
||||
self.connected = true
|
||||
|
||||
if self.nsp != nil {
|
||||
// Join namespace
|
||||
self.joinNamespace()
|
||||
return
|
||||
}
|
||||
|
||||
// Don't handle as internal because something crazy could happen where
|
||||
// we disconnect before it's handled
|
||||
self.handleEvent("connect", data: nil)
|
||||
}
|
||||
|
||||
// Called when the socket is closed
|
||||
func webSocket(webSocket:SRWebSocket!, didCloseWithCode code:Int, reason:String!, wasClean:Bool) {
|
||||
self.pingTimer?.invalidate()
|
||||
func webSocketDidCloseWithCode(code:Int, reason:String!, wasClean:Bool) {
|
||||
self.connected = false
|
||||
self.connecting = false
|
||||
if self.closed || !self.reconnects {
|
||||
self.handleEvent("disconnect", data: reason, isInternalMessage: true)
|
||||
self.didForceClose()
|
||||
} else {
|
||||
self.handleEvent("reconnect", data: reason, isInternalMessage: true)
|
||||
self.tryReconnect(triesLeft: self.reconnectAttempts)
|
||||
|
||||
self.tryReconnect()
|
||||
}
|
||||
}
|
||||
|
||||
// Called when an error occurs.
|
||||
func webSocket(webSocket:SRWebSocket!, didFailWithError error:NSError!) {
|
||||
self.pingTimer?.invalidate()
|
||||
func webSocketDidFailWithError(error:NSError!) {
|
||||
self.connected = false
|
||||
self.connecting = false
|
||||
self.handleEvent("error", data: error.localizedDescription, isInternalMessage: true)
|
||||
if self.closed || !self.reconnects {
|
||||
self.handleEvent("disconnect", data: error.localizedDescription, isInternalMessage: true)
|
||||
self.didForceClose()
|
||||
} else if !self.reconnecting {
|
||||
self.handleEvent("reconnect", data: error.localizedDescription, isInternalMessage: true)
|
||||
self.tryReconnect(triesLeft: self.reconnectAttempts)
|
||||
self.tryReconnect()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user