diff --git a/README.md b/README.md index b46d195..ff2aed8 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,8 @@ let socket = SocketIOClient(socketURL: "https://localhost:8080", opts: [ "reconnectAttempts": 5, // Default is -1 (infinite tries) "reconnectWait": 5, // Default is 10 "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) + "forcePolling": true, // if true the client will only use XHR polling, Default is false (polling/WebSockets) + "forceWebsockets": false, // if true the client will only use WebSockets. Trumps forcePolling. Default is false. (polling/WebSockets) "cookies": nil // An array of NSHTTPCookies. Passed during handshake. Default is nil ]) diff --git a/Socket.IO-Client-Swift.podspec b/Socket.IO-Client-Swift.podspec index ab5261a..a350c05 100644 --- a/Socket.IO-Client-Swift.podspec +++ b/Socket.IO-Client-Swift.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Socket.IO-Client-Swift" - s.version = "1.4.2" + s.version = "1.4.3" s.summary = "Socket.IO-client for Swift" s.description = <<-DESC Socket.IO-client for Swift. @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.author = { "Erik" => "nuclear.ace@gmail.com" } s.ios.deployment_target = '8.0' s.osx.deployment_target = '10.10' - s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v1.4.2' } + s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v1.4.3' } s.source_files = "SwiftIO/**/*.swift" s.requires_arc = true # s.dependency 'Starscream', '~> 0.9' # currently this repo includes Starscream swift files diff --git a/SwiftIO/SocketEngine.swift b/SwiftIO/SocketEngine.swift index 49f6275..9c2191c 100644 --- a/SwiftIO/SocketEngine.swift +++ b/SwiftIO/SocketEngine.swift @@ -57,6 +57,7 @@ public class SocketEngine: NSObject, WebSocketDelegate { private var _connected = false private var fastUpgrade = false private var forcePolling = false + private var forceWebsockets = false private var pingTimer:NSTimer? private var postWait = [String]() private var _polling = true @@ -82,12 +83,14 @@ public class SocketEngine: NSObject, WebSocketDelegate { } var ws:WebSocket? - public init(client:SocketEngineClient, forcePolling:Bool, withCookies cookies:[NSHTTPCookie]?) { - self.client = client - self.forcePolling = forcePolling - self.cookies = cookies - self.session = NSURLSession(configuration: NSURLSessionConfiguration.ephemeralSessionConfiguration(), - delegate: nil, delegateQueue: self.workQueue) + public init(client:SocketEngineClient, forcePolling:Bool, + forceWebsockets:Bool, withCookies cookies:[NSHTTPCookie]?) { + self.client = client + self.forcePolling = forcePolling + self.forceWebsockets = forceWebsockets + self.cookies = cookies + self.session = NSURLSession(configuration: NSURLSessionConfiguration.ephemeralSessionConfiguration(), + delegate: nil, delegateQueue: self.workQueue) } public func close(#fast:Bool) { @@ -157,6 +160,16 @@ public class SocketEngine: NSObject, WebSocketDelegate { return (urlPolling, urlWebSocket) } + private func createWebsocket(andConnect connect:Bool) { + self.ws = WebSocket(url: NSURL(string: self.urlWebSocket! + "&sid=\(self.sid)")!) + self.ws?.queue = self.handleQueue + self.ws?.delegate = self + + if connect { + self.ws?.connect() + } + } + private func doFastUpgrade() { self.sendWebSocketMessage("", withType: PacketType.UPGRADE) self._websocket = true @@ -171,16 +184,16 @@ public class SocketEngine: NSObject, WebSocketDelegate { } self.waitingForPoll = true - self.doRequest(self.parsePollingMessage) + let req = NSURLRequest(URL: NSURL(string: self.urlPolling! + "&sid=\(self.sid)&b64=1")!) + + self.doRequest(req) } - private func doRequest(callback:(String) -> Void) { + private func doRequest(req:NSURLRequest) { if !self.polling { return } - let req = NSURLRequest(URL: NSURL(string: self.urlPolling! + "&sid=\(self.sid)")!) - self.session.dataTaskWithRequest(req) {[weak self] data, res, err in if self == nil { return @@ -196,7 +209,10 @@ public class SocketEngine: NSObject, WebSocketDelegate { if let str = NSString(data: data, encoding: NSUTF8StringEncoding) as? String { - dispatch_async(self!.parseQueue) {callback(str)} + dispatch_async(self!.parseQueue) { + self?.parsePollingMessage(str) + return + } } self?.waitingForPoll = false @@ -307,6 +323,14 @@ public class SocketEngine: NSObject, WebSocketDelegate { let (urlPolling, urlWebSocket) = self.createURLs(opts) self.urlPolling = urlPolling self.urlWebSocket = urlWebSocket + + if self.forceWebsockets { + self._polling = false + self._websocket = true + self.createWebsocket(andConnect: true) + return + } + let reqPolling = NSMutableURLRequest(URL: NSURL(string: urlPolling + "&b64=1")!) if self.cookies != nil { @@ -314,57 +338,7 @@ public class SocketEngine: NSObject, WebSocketDelegate { reqPolling.allHTTPHeaderFields = headers } - 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.localizedDescription) - return - } - - if let dataString = NSString(data: data, encoding: NSUTF8StringEncoding) as? String { - let parsed:[String]? = dataString["(\\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 - } - - 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 - self?._connected = true - - if !self!.forcePolling { - self?.ws = WebSocket(url: NSURL(string: urlWebSocket + "&sid=\(self!.sid)")!) - self?.ws?.queue = self?.handleQueue - self?.ws?.delegate = self - self?.ws?.connect() - } - } else { - NSLog("Error handshaking") - return - } - - if let pingInterval = json["pingInterval"] as? Int { - self?.pingInterval = pingInterval / 1000 - } - } - - self?.doPoll() - self?.startPingTimer() - }}.resume() + self.doRequest(reqPolling) } // Translatation of engine.io-parser#decodePayload @@ -413,7 +387,7 @@ public class SocketEngine: NSObject, WebSocketDelegate { if msg.length != 0 { // Be sure to capture the value of the msg dispatch_async(self.handleQueue) {[weak self, msg] in - self?.parseEngineMessage(msg) + self?.parseEngineMessage(msg, fromPolling: true) return } } @@ -431,9 +405,9 @@ public class SocketEngine: NSObject, WebSocketDelegate { } } - private func parseEngineMessage(var message:String) { + private func parseEngineMessage(var message:String, fromPolling:Bool) { // NSLog("Engine got message: \(message)") - if self.polling { + if fromPolling { fixDoubleUTF8(&message) } @@ -466,6 +440,40 @@ public class SocketEngine: NSObject, WebSocketDelegate { self.upgradeTransport() return } + } else if type == PacketType.OPEN.rawValue { + var err:NSError? + + message.removeAtIndex(message.startIndex) + let mesData = message.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! + + if let json = NSJSONSerialization.JSONObjectWithData(mesData, + options: NSJSONReadingOptions.AllowFragments, error: &err) as? NSDictionary { + if let sid = json["sid"] as? String { + // println(json) + self.sid = sid + self._connected = true + if !self.forcePolling && !self.forceWebsockets { + self.createWebsocket(andConnect: true) + } + } else { + NSLog("Error handshaking") + return + } + + if let pingInterval = json["pingInterval"] as? Int { + self.pingInterval = pingInterval / 1000 + } + } else { + fatalError("Error parsing engine connect") + } + + self.startPingTimer() + + if !self.forceWebsockets { + self.doPoll() + } + + return } else if type == PacketType.CLOSE.rawValue { if self.polling { self.client.didForceClose("Disconnect") @@ -588,8 +596,15 @@ public class SocketEngine: NSObject, WebSocketDelegate { public func websocketDidConnect(socket:WebSocket) { self.websocketConnected = true - self.probing = true - self.probeWebSocket() + + if !self.forceWebsockets { + self.probing = true + self.probeWebSocket() + } else { + self._connected = true + self.probing = false + self._polling = false + } } public func websocketDidDisconnect(socket:WebSocket, error:NSError?) { @@ -600,15 +615,17 @@ public class SocketEngine: NSObject, WebSocketDelegate { self.pingTimer?.invalidate() self._connected = false self._websocket = false - self._polling = true - self.client.webSocketDidCloseWithCode(1, reason: "Socket Disconnect", wasClean: true) + + let reason = error?.localizedDescription + self.client.webSocketDidCloseWithCode(1, + reason: reason == nil ? "Socket Disconnected" : reason!) } else { self.flushProbeWait() } } public func websocketDidReceiveMessage(socket:WebSocket, text:String) { - self.parseEngineMessage(text) + self.parseEngineMessage(text, fromPolling: false) } public func websocketDidReceiveData(socket:WebSocket, data:NSData) { diff --git a/SwiftIO/SocketEngineClient.swift b/SwiftIO/SocketEngineClient.swift index 57c5c1e..3334397 100644 --- a/SwiftIO/SocketEngineClient.swift +++ b/SwiftIO/SocketEngineClient.swift @@ -36,6 +36,6 @@ import Foundation func parseSocketMessage(msg:String) func parseBinaryData(data:NSData) func pollingDidFail(err:String) - func webSocketDidCloseWithCode(code:Int, reason:String, wasClean:Bool) + func webSocketDidCloseWithCode(code:Int, reason:String) func webSocketDidFailWithError(error:NSError) } \ No newline at end of file diff --git a/SwiftIO/SocketIOClient.swift b/SwiftIO/SocketIOClient.swift index 144925b..91f4711 100644 --- a/SwiftIO/SocketIOClient.swift +++ b/SwiftIO/SocketIOClient.swift @@ -34,6 +34,7 @@ public class SocketIOClient: NSObject, SocketEngineClient { private var _connecting = false private var currentReconnectAttempt = 0 private var forcePolling = false + private var forceWebsockets = false private var handlers = ContiguousArray() private var paramConnect = false private var _secure = false @@ -102,7 +103,11 @@ public class SocketIOClient: NSObject, SocketEngineClient { self.reconnectWait = abs(reconnectWait) } - if let nsp = opts!["nsp"] as? String { + if var nsp = opts!["nsp"] as? String { + if nsp != "/" && nsp.hasPrefix("/") { + nsp.removeAtIndex(nsp.startIndex) + } + self.nsp = nsp } @@ -110,6 +115,10 @@ public class SocketIOClient: NSObject, SocketEngineClient { self.forcePolling = polling } + if let ws = opts!["forceWebsockets"] as? Bool { + self.forceWebsockets = ws + } + if let cookies = opts!["cookies"] as? [NSHTTPCookie] { self.cookies = cookies } @@ -127,6 +136,7 @@ public class SocketIOClient: NSObject, SocketEngineClient { private func addEngine() { self.engine = SocketEngine(client: self, forcePolling: self.forcePolling, + forceWebsockets: self.forceWebsockets, withCookies: self.cookies) } @@ -152,6 +162,10 @@ public class SocketIOClient: NSObject, SocketEngineClient { self._closed = false } + if self.connected { + return + } + self.addEngine() self.engine?.open() } @@ -165,6 +179,10 @@ public class SocketIOClient: NSObject, SocketEngineClient { self._closed = false } + if self.connected { + return + } + self.params = params self.paramConnect = true @@ -334,7 +352,7 @@ public class SocketIOClient: NSObject, SocketEngineClient { Causes an event to be handled. Only use if you know what you're doing. */ public func handleEvent(event:String, data:[AnyObject]?, isInternalMessage:Bool = false, - wantsAck ack:Int? = nil, withAckType ackType:Int = 3) { + wantsAck ack:Int? = nil) { // println("Should do event: \(event) with data: \(data)") if !self.connected && !isInternalMessage { return @@ -350,8 +368,7 @@ public class SocketIOClient: NSObject, SocketEngineClient { for handler in self.handlers { if handler.event == event { if ack != nil { - handler.executeCallback(data, withAck: ack!, - withAckType: ackType, withSocket: self) + handler.executeCallback(data, withAck: ack!, withSocket: self) } else { handler.executeCallback(data) } @@ -452,12 +469,12 @@ public class SocketIOClient: NSObject, SocketEngineClient { } // Called when the socket is closed - public func webSocketDidCloseWithCode(code:Int, reason:String, wasClean:Bool) { + public func webSocketDidCloseWithCode(code:Int, reason:String) { self._connected = false self._connecting = false if self.closed || !self.reconnects { self.didForceClose("WebSocket closed") - } else { + } else if !self.reconnecting { self.handleEvent("reconnect", data: [reason], isInternalMessage: true) self.tryReconnect() } diff --git a/SwiftIO/SocketPacket.swift b/SwiftIO/SocketPacket.swift index 88bd394..0681b8f 100644 --- a/SwiftIO/SocketPacket.swift +++ b/SwiftIO/SocketPacket.swift @@ -134,12 +134,16 @@ class SocketPacket { var msg:String if self.binary.count == 0 { + self.type = SocketPacketType.ACK + if nsp == "/" { msg = "3\(self.id!)[" } else { msg = "3/\(self.nsp),\(self.id!)[" } } else { + self.type = SocketPacketType.BINARY_ACK + if nsp == "/" { msg = "6\(self.binary.count)-\(self.id!)[" } else { @@ -194,8 +198,6 @@ class SocketPacket { if let str = self.data?[i] as? String { if let num = str["~~(\\d)"].groups() { newArr[i] = self.binary[num[1].toInt()!] - } else { - newArr[i] = str } } else if self.data?[i] is NSDictionary || self.data?[i] is NSArray { newArr[i] = self._fillInPlaceholders(self.data![i]) diff --git a/SwiftIO/SocketParser.swift b/SwiftIO/SocketParser.swift index a28f585..8dbd04e 100644 --- a/SwiftIO/SocketParser.swift +++ b/SwiftIO/SocketParser.swift @@ -24,7 +24,7 @@ import Foundation class SocketParser { private static let shredder = SocketParser.PacketShredder() - + // Translation of socket.io-parser#deconstructPacket private class PacketShredder { var buf = ContiguousArray() @@ -197,7 +197,8 @@ class SocketParser { return } - socket.handleEvent(p.getEvent(), data: p.data, isInternalMessage: false, wantsAck: p.id, withAckType: 3) + socket.handleEvent(p.getEvent(), data: p.data, + isInternalMessage: false, wantsAck: p.id) } else if p.type == SocketPacketType.ACK { if checkNSP(p.nsp) { return @@ -250,7 +251,7 @@ class SocketParser { if !packet.justAck { socket.handleEvent(packet.getEvent(), data: packet.data, - wantsAck: packet.id, withAckType: 6) + wantsAck: packet.id) } else { socket.handleAck(packet.id!, data: packet.data) }