From 62131ef9045c53446ed92853fc39dfc5ee55d84e Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 3 Mar 2016 09:57:39 -0500 Subject: [PATCH 1/8] make sure we don't handle poll messages if we aren't polling --- Source/SocketEnginePollable.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/SocketEnginePollable.swift b/Source/SocketEnginePollable.swift index 249f4e4..9d84c55 100644 --- a/Source/SocketEnginePollable.swift +++ b/Source/SocketEnginePollable.swift @@ -112,7 +112,7 @@ extension SocketEnginePollable { func doLongPoll(req: NSURLRequest) { doRequest(req) {[weak self] data, res, err in - guard let this = self else { return } + guard let this = self where this.polling else { return } if err != nil || data == nil { DefaultSocketLogger.Logger.error(err?.localizedDescription ?? "Error", type: "SocketEnginePolling") @@ -123,7 +123,7 @@ extension SocketEnginePollable { return } - + DefaultSocketLogger.Logger.log("Got polling response", type: "SocketEnginePolling") if let str = String(data: data!, encoding: NSUTF8StringEncoding) { From 2f7a5405b01119da80aa175592f2aaddfd002172 Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 3 Mar 2016 09:59:33 -0500 Subject: [PATCH 2/8] add check for fast upgrade --- Source/SocketEnginePollable.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/SocketEnginePollable.swift b/Source/SocketEnginePollable.swift index 9d84c55..354ed7d 100644 --- a/Source/SocketEnginePollable.swift +++ b/Source/SocketEnginePollable.swift @@ -100,7 +100,7 @@ extension SocketEnginePollable { } func doRequest(req: NSURLRequest, withCallback callback: (NSData?, NSURLResponse?, NSError?) -> Void) { - if !polling || closed || invalidated { + if !polling || closed || invalidated || fastUpgrade { DefaultSocketLogger.Logger.error("Tried to do polling request when not supposed to", type: "SocketEnginePolling") return } From 6da9463c8d1794629e37ec76e1538e737fffa8ec Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 3 Mar 2016 10:11:08 -0500 Subject: [PATCH 3/8] whitespace --- Source/SocketEnginePollable.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/SocketEnginePollable.swift b/Source/SocketEnginePollable.swift index 354ed7d..355429a 100644 --- a/Source/SocketEnginePollable.swift +++ b/Source/SocketEnginePollable.swift @@ -100,14 +100,14 @@ extension SocketEnginePollable { } func doRequest(req: NSURLRequest, withCallback callback: (NSData?, NSURLResponse?, NSError?) -> Void) { - if !polling || closed || invalidated || fastUpgrade { - DefaultSocketLogger.Logger.error("Tried to do polling request when not supposed to", type: "SocketEnginePolling") - return - } - - DefaultSocketLogger.Logger.log("Doing polling request", type: "SocketEnginePolling") - - session?.dataTaskWithRequest(req, completionHandler: callback).resume() + if !polling || closed || invalidated || fastUpgrade { + DefaultSocketLogger.Logger.error("Tried to do polling request when not supposed to", type: "SocketEnginePolling") + return + } + + DefaultSocketLogger.Logger.log("Doing polling request", type: "SocketEnginePolling") + + session?.dataTaskWithRequest(req, completionHandler: callback).resume() } func doLongPoll(req: NSURLRequest) { From 76a572e73584a0b1290259718da200fc5f499acf Mon Sep 17 00:00:00 2001 From: Erik Date: Fri, 11 Mar 2016 13:03:40 -0500 Subject: [PATCH 4/8] refactor reconnect --- Source/SocketIOClient.swift | 126 ++++++++++++------------------ Source/SocketIOClientStatus.swift | 4 +- 2 files changed, 49 insertions(+), 81 deletions(-) diff --git a/Source/SocketIOClient.swift b/Source/SocketIOClient.swift index d3a0b36..ccfa824 100644 --- a/Source/SocketIOClient.swift +++ b/Source/SocketIOClient.swift @@ -28,7 +28,17 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable public let socketURL: NSURL public private(set) var engine: SocketEngineSpec? - public private(set) var status = SocketIOClientStatus.NotConnected + public private(set) var status = SocketIOClientStatus.NotConnected { + didSet { + switch status { + case .Connected: + reconnecting = false + currentReconnectAttempt = 0 + default: + break + } + } + } public var forceNew = false public var nsp = "/" @@ -46,8 +56,8 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable private var anyHandler: ((SocketAnyEvent) -> Void)? private var currentReconnectAttempt = 0 private var handlers = [SocketEventHandler]() - private var reconnectTimer: NSTimer? private var ackHandlers = SocketAckManager() + private var reconnecting = false private(set) var currentAck = -1 private(set) var handleQueue = dispatch_get_main_queue() @@ -55,9 +65,7 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable var waitingPackets = [SocketPacket]() - /** - Type safe way to create a new SocketIOClient. opts can be omitted - */ + /// Type safe way to create a new SocketIOClient. opts can be omitted public init(socketURL: NSURL, options: Set = []) { self.options = options self.socketURL = socketURL @@ -94,10 +102,8 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable super.init() } - /** - Not so type safe way to create a SocketIOClient, meant for Objective-C compatiblity. - If using Swift it's recommended to use `init(socketURL: NSURL, options: Set)` - */ + /// Not so type safe way to create a SocketIOClient, meant for Objective-C compatiblity. + /// If using Swift it's recommended to use `init(socketURL: NSURL, options: Set)` public convenience init(socketURL: NSURL, options: NSDictionary?) { self.init(socketURL: socketURL, options: options?.toSocketOptionsSet() ?? []) } @@ -127,26 +133,17 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable return engine! } - private func clearReconnectTimer() { - reconnectTimer?.invalidate() - reconnectTimer = nil - } - @available(*, deprecated=5.3, message="Please use disconnect()") public func close() { disconnect() } - /** - Connect to the server. - */ + /// Connect to the server. public func connect() { connect(timeoutAfter: 0, withTimeoutHandler: nil) } - /** - Connect to the server. If we aren't connected after timeoutAfter, call handler - */ + /// Connect to the server. If we aren't connected after timeoutAfter, call handler public func connect(timeoutAfter timeoutAfter: Int, withTimeoutHandler handler: (() -> Void)?) { assert(timeoutAfter >= 0, "Invalid timeout: \(timeoutAfter)") @@ -203,8 +200,6 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable func didConnect() { DefaultSocketLogger.Logger.log("Socket connected", type: logType) status = .Connected - currentReconnectAttempt = 0 - clearReconnectTimer() // Don't handle as internal because something crazy could happen where // we disconnect before it's handled @@ -224,10 +219,8 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable handleEvent("disconnect", data: [reason], isInternalMessage: true) } - /** - Disconnects the socket. Only reconnect the same socket if you know what you're doing. - Will turn off automatic reconnects. - */ + /// Disconnects the socket. Only reconnect the same socket if you know what you're doing. + /// Will turn off automatic reconnects. public func disconnect() { DefaultSocketLogger.Logger.log("Closing socket", type: logType) @@ -235,16 +228,12 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable didDisconnect("Disconnect") } - /** - Send a message to the server - */ + /// Send a message to the server public func emit(event: String, _ items: AnyObject...) { emit(event, withItems: items) } - /** - Same as emit, but meant for Objective-C - */ + /// Same as emit, but meant for Objective-C public func emit(event: String, withItems items: [AnyObject]) { guard status == .Connected else { handleEvent("error", data: ["Tried emitting \(event) when not connected"], isInternalMessage: true) @@ -301,10 +290,15 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable public func engineDidClose(reason: String) { waitingPackets.removeAll() + + if status != .Closed { + status = .NotConnected + } if status == .Closed || !reconnects { didDisconnect(reason) - } else if status != .Reconnecting { + } else if !reconnecting { + reconnecting = true tryReconnectWithReason(reason) } } @@ -325,9 +319,7 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable ackHandlers.executeAck(ack, items: data) } - /** - Causes an event to be handled. Only use if you know what you're doing. - */ + /// Causes an event to be handled. Only use if you know what you're doing. public func handleEvent(event: String, data: [AnyObject], isInternalMessage: Bool, withAck ack: Int = -1) { guard status == .Connected || isInternalMessage else { return @@ -344,9 +336,7 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable } } - /** - Leaves nsp and goes back to / - */ + /// Leaves nsp and goes back to / public func leaveNamespace() { if nsp != "/" { engine?.send("1\(nsp)", withData: []) @@ -354,9 +344,7 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable } } - /** - Joins namespace - */ + /// Joins namespace public func joinNamespace(namespace: String) { nsp = namespace @@ -366,28 +354,22 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable } } - /** - Removes handler(s) - */ + /// Removes handler(s) based on name public func off(event: String) { DefaultSocketLogger.Logger.log("Removing handler for event: %@", type: logType, args: event) handlers = handlers.filter { $0.event != event } } - /** - Removes a handler with the specified UUID gotten from an `on` or `once` - */ + /// Removes a handler with the specified UUID gotten from an `on` or `once` public func off(id id: NSUUID) { DefaultSocketLogger.Logger.log("Removing handler with id: %@", type: logType, args: id) handlers = handlers.filter { $0.id != id } } - /** - Adds a handler for an event. - Returns: A unique id for the handler - */ + /// Adds a handler for an event. + /// Returns: A unique id for the handler public func on(event: String, callback: NormalCallback) -> NSUUID { DefaultSocketLogger.Logger.log("Adding handler for event: %@", type: logType, args: event) @@ -397,10 +379,8 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable return handler.id } - /** - Adds a single-use handler for an event. - Returns: A unique id for the handler - */ + /// Adds a single-use handler for an event. + /// Returns: A unique id for the handler public func once(event: String, callback: NormalCallback) -> NSUUID { DefaultSocketLogger.Logger.log("Adding once handler for event: %@", type: logType, args: event) @@ -417,9 +397,7 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable return handler.id } - /** - Adds a handler that will be called on every event. - */ + /// Adds a handler that will be called on every event. public func onAny(handler: (SocketAnyEvent) -> Void) { anyHandler = handler } @@ -443,44 +421,32 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable } } - /** - Tries to reconnect to the server. - */ + /// Tries to reconnect to the server. public func reconnect() { tryReconnectWithReason("manual reconnect") } - /** - Removes all handlers. - Can be used after disconnecting to break any potential remaining retain cycles. - */ + /// Removes all handlers. + /// Can be used after disconnecting to break any potential remaining retain cycles. public func removeAllHandlers() { handlers.removeAll(keepCapacity: false) } private func tryReconnectWithReason(reason: String) { - if reconnectTimer == nil { + if reconnecting { DefaultSocketLogger.Logger.log("Starting reconnect", type: logType) handleEvent("reconnect", data: [reason], isInternalMessage: true) - - status = .Reconnecting - - dispatch_async(dispatch_get_main_queue()) { - self.reconnectTimer = NSTimer.scheduledTimerWithTimeInterval(Double(self.reconnectWait), - target: self, selector: "_tryReconnect", userInfo: nil, repeats: true) - } + + _tryReconnect() } } @objc private func _tryReconnect() { - if status == .Connected { - clearReconnectTimer() - + if !reconnecting { return } if reconnectAttempts != -1 && currentReconnectAttempt + 1 > reconnectAttempts || !reconnects { - clearReconnectTimer() didDisconnect("Reconnect Failed") return @@ -492,6 +458,10 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable currentReconnectAttempt += 1 connect() + + let dispatchAfter = dispatch_time(DISPATCH_TIME_NOW, Int64(UInt64(reconnectWait) * NSEC_PER_SEC)) + + dispatch_after(dispatchAfter, dispatch_get_main_queue(), _tryReconnect) } } diff --git a/Source/SocketIOClientStatus.swift b/Source/SocketIOClientStatus.swift index e8720c6..13e82b9 100644 --- a/Source/SocketIOClientStatus.swift +++ b/Source/SocketIOClientStatus.swift @@ -25,7 +25,7 @@ import Foundation @objc public enum SocketIOClientStatus: Int, CustomStringConvertible { - case NotConnected, Closed, Connecting, Connected, Reconnecting + case NotConnected, Closed, Connecting, Connected public var description: String { switch self { @@ -37,8 +37,6 @@ import Foundation return "Connecting" case Connected: return "Connected" - case Reconnecting: - return "Reconnecting" } } } \ No newline at end of file From 596a499041ec13bdf14beba9ebb1a28fd5f892df Mon Sep 17 00:00:00 2001 From: Erik Date: Fri, 11 Mar 2016 13:41:09 -0500 Subject: [PATCH 5/8] work on fixing reconnect() --- Source/SocketEngine.swift | 4 ++++ Source/SocketIOClient.swift | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Source/SocketEngine.swift b/Source/SocketEngine.swift index 331bcab..07e12f1 100644 --- a/Source/SocketEngine.swift +++ b/Source/SocketEngine.swift @@ -301,6 +301,10 @@ public final class SocketEngine: NSObject, SocketEnginePollable, SocketEngineWeb client?.engineDidClose(reason) } + if closed { + return postSendClose(nil, nil, nil) + } + DefaultSocketLogger.Logger.log("Engine is being closed.", type: logType) if websocket { diff --git a/Source/SocketIOClient.swift b/Source/SocketIOClient.swift index ccfa824..981f744 100644 --- a/Source/SocketIOClient.swift +++ b/Source/SocketIOClient.swift @@ -423,7 +423,9 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable /// Tries to reconnect to the server. public func reconnect() { - tryReconnectWithReason("manual reconnect") + guard !reconnecting else { return } + + engine?.disconnect("manual reconnect") } /// Removes all handlers. From 3a842469ec8724ab6400cc5cde1062d9f44bf0f4 Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 16 Mar 2016 13:13:22 -0400 Subject: [PATCH 6/8] fix log placement --- Source/SocketEngine.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/SocketEngine.swift b/Source/SocketEngine.swift index 07e12f1..2f757b5 100644 --- a/Source/SocketEngine.swift +++ b/Source/SocketEngine.swift @@ -301,12 +301,12 @@ public final class SocketEngine: NSObject, SocketEnginePollable, SocketEngineWeb client?.engineDidClose(reason) } + DefaultSocketLogger.Logger.log("Engine is being closed.", type: logType) + if closed { return postSendClose(nil, nil, nil) } - DefaultSocketLogger.Logger.log("Engine is being closed.", type: logType) - if websocket { sendWebSocketMessage("", withType: .Close, withData: []) postSendClose(nil, nil, nil) From 6c6b4f32c9a6bb3a47984480d4c37d078cd15790 Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 22 Mar 2016 09:00:27 -0400 Subject: [PATCH 7/8] Update for swift 2.2 --- SocketIO-MacTests/SocketParserTest.swift | 2 +- Source/SocketEngine.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SocketIO-MacTests/SocketParserTest.swift b/SocketIO-MacTests/SocketParserTest.swift index d17bb44..9513fcd 100644 --- a/SocketIO-MacTests/SocketParserTest.swift +++ b/SocketIO-MacTests/SocketParserTest.swift @@ -113,7 +113,7 @@ class SocketParserTest: XCTestCase { func validateParseResult(message: String) { let validValues = SocketParserTest.packetTypes[message]! let packet = testSocket.parseString(message) - let type = message.substringWithRange(Range(start: message.startIndex, end: message.startIndex.advancedBy(1))) + let type = message.substringWithRange(Range(message.startIndex.. Date: Tue, 22 Mar 2016 09:05:57 -0400 Subject: [PATCH 8/8] update version and readme --- README.md | 10 ++++++---- Socket.IO-Client-Swift.podspec | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c5d6b91..73f2882 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,9 @@ SocketIOClient* socket = [[SocketIOClient alloc] initWithSocketURL:url options:@ - Can be used from Objective-C ##Installation -Requires Swift 2/Xcode 7 +Requires Swift 2.2/Xcode 7.3 + +If you need Swift 2.1/Xcode 7.2 use v5.5.0 (Pre-Swift 2.2 support is no longer maintained) If you need Swift 1.2/Xcode 6.3/4 use v2.4.5 (Pre-Swift 2 support is no longer maintained) @@ -86,7 +88,7 @@ Carthage ----------------- Add this line to your `Cartfile`: ``` -github "socketio/socket.io-client-swift" ~> 5.5.0 # Or latest version +github "socketio/socket.io-client-swift" ~> 6.0.0 # Or latest version ``` Run `carthage update --platform ios,macosx`. @@ -100,7 +102,7 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' use_frameworks! -pod 'Socket.IO-Client-Swift', '~> 5.5.0' # Or latest version +pod 'Socket.IO-Client-Swift', '~> 6.0.0' # Or latest version ``` Install pods: @@ -128,7 +130,7 @@ CocoaSeeds Add this line to your `Seedfile`: ``` -github "socketio/socket.io-client-swift", "v5.5.0", :files => "Source/*.swift" # Or latest version +github "socketio/socket.io-client-swift", "v6.0.0", :files => "Source/*.swift" # Or latest version ``` Run `seed install`. diff --git a/Socket.IO-Client-Swift.podspec b/Socket.IO-Client-Swift.podspec index 0b5e3ca..42a8ef9 100644 --- a/Socket.IO-Client-Swift.podspec +++ b/Socket.IO-Client-Swift.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "Socket.IO-Client-Swift" s.module_name = "SocketIOClientSwift" - s.version = "5.5.0" + s.version = "6.0.0" s.summary = "Socket.IO-client for iOS and OS X" s.description = <<-DESC Socket.IO-client for iOS and OS X. @@ -14,7 +14,7 @@ Pod::Spec.new do |s| s.ios.deployment_target = '8.0' s.osx.deployment_target = '10.10' s.tvos.deployment_target = '9.0' - s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v5.5.0' } + s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v6.0.0' } s.source_files = "Source/**/*.swift" s.requires_arc = true # s.dependency 'Starscream', '~> 0.9' # currently this repo includes Starscream swift files