Merge pull request #330 from socketio/development

Development
This commit is contained in:
Erik Little 2016-03-22 09:06:13 -04:00
commit 2919d4a5ce
7 changed files with 76 additions and 100 deletions

View File

@ -55,7 +55,9 @@ SocketIOClient* socket = [[SocketIOClient alloc] initWithSocketURL:url options:@
- Can be used from Objective-C - Can be used from Objective-C
##Installation ##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) 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`: 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`. Run `carthage update --platform ios,macosx`.
@ -100,7 +102,7 @@ source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0' platform :ios, '8.0'
use_frameworks! 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: Install pods:
@ -128,7 +130,7 @@ CocoaSeeds
Add this line to your `Seedfile`: 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`. Run `seed install`.

View File

@ -1,7 +1,7 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = "Socket.IO-Client-Swift" s.name = "Socket.IO-Client-Swift"
s.module_name = "SocketIOClientSwift" 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.summary = "Socket.IO-client for iOS and OS X"
s.description = <<-DESC s.description = <<-DESC
Socket.IO-client for iOS and OS X. Socket.IO-client for iOS and OS X.
@ -14,7 +14,7 @@ Pod::Spec.new do |s|
s.ios.deployment_target = '8.0' s.ios.deployment_target = '8.0'
s.osx.deployment_target = '10.10' s.osx.deployment_target = '10.10'
s.tvos.deployment_target = '9.0' 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.source_files = "Source/**/*.swift"
s.requires_arc = true s.requires_arc = true
# s.dependency 'Starscream', '~> 0.9' # currently this repo includes Starscream swift files # s.dependency 'Starscream', '~> 0.9' # currently this repo includes Starscream swift files

View File

@ -113,7 +113,7 @@ class SocketParserTest: XCTestCase {
func validateParseResult(message: String) { func validateParseResult(message: String) {
let validValues = SocketParserTest.packetTypes[message]! let validValues = SocketParserTest.packetTypes[message]!
let packet = testSocket.parseString(message) let packet = testSocket.parseString(message)
let type = message.substringWithRange(Range<String.Index>(start: message.startIndex, end: message.startIndex.advancedBy(1))) let type = message.substringWithRange(Range<String.Index>(message.startIndex..<message.startIndex.advancedBy(1)))
if case let .Right(packet) = packet { if case let .Right(packet) = packet {
XCTAssertEqual(packet.type, SocketPacket.PacketType(rawValue: Int(type) ?? -1)!) XCTAssertEqual(packet.type, SocketPacket.PacketType(rawValue: Int(type) ?? -1)!)
XCTAssertEqual(packet.nsp, validValues.0) XCTAssertEqual(packet.nsp, validValues.0)

View File

@ -303,6 +303,10 @@ public final class SocketEngine: NSObject, SocketEnginePollable, SocketEngineWeb
DefaultSocketLogger.Logger.log("Engine is being closed.", type: logType) DefaultSocketLogger.Logger.log("Engine is being closed.", type: logType)
if closed {
return postSendClose(nil, nil, nil)
}
if websocket { if websocket {
sendWebSocketMessage("", withType: .Close, withData: []) sendWebSocketMessage("", withType: .Close, withData: [])
postSendClose(nil, nil, nil) postSendClose(nil, nil, nil)
@ -502,7 +506,7 @@ public final class SocketEngine: NSObject, SocketEnginePollable, SocketEngineWeb
dispatch_async(dispatch_get_main_queue()) { dispatch_async(dispatch_get_main_queue()) {
self.pingTimer = NSTimer.scheduledTimerWithTimeInterval(pingInterval, target: self, self.pingTimer = NSTimer.scheduledTimerWithTimeInterval(pingInterval, target: self,
selector: Selector("sendPing"), userInfo: nil, repeats: true) selector: #selector(SocketEngine.sendPing), userInfo: nil, repeats: true)
} }
} }
} }

View File

@ -100,7 +100,7 @@ extension SocketEnginePollable {
} }
func doRequest(req: NSURLRequest, withCallback callback: (NSData?, NSURLResponse?, NSError?) -> Void) { 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") DefaultSocketLogger.Logger.error("Tried to do polling request when not supposed to", type: "SocketEnginePolling")
return return
} }
@ -112,7 +112,7 @@ extension SocketEnginePollable {
func doLongPoll(req: NSURLRequest) { func doLongPoll(req: NSURLRequest) {
doRequest(req) {[weak self] data, res, err in 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 { if err != nil || data == nil {
DefaultSocketLogger.Logger.error(err?.localizedDescription ?? "Error", type: "SocketEnginePolling") DefaultSocketLogger.Logger.error(err?.localizedDescription ?? "Error", type: "SocketEnginePolling")

View File

@ -28,7 +28,17 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable
public let socketURL: NSURL public let socketURL: NSURL
public private(set) var engine: SocketEngineSpec? 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 forceNew = false
public var nsp = "/" public var nsp = "/"
@ -46,8 +56,8 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable
private var anyHandler: ((SocketAnyEvent) -> Void)? private var anyHandler: ((SocketAnyEvent) -> Void)?
private var currentReconnectAttempt = 0 private var currentReconnectAttempt = 0
private var handlers = [SocketEventHandler]() private var handlers = [SocketEventHandler]()
private var reconnectTimer: NSTimer?
private var ackHandlers = SocketAckManager() private var ackHandlers = SocketAckManager()
private var reconnecting = false
private(set) var currentAck = -1 private(set) var currentAck = -1
private(set) var handleQueue = dispatch_get_main_queue() private(set) var handleQueue = dispatch_get_main_queue()
@ -55,9 +65,7 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable
var waitingPackets = [SocketPacket]() 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<SocketIOClientOption> = []) { public init(socketURL: NSURL, options: Set<SocketIOClientOption> = []) {
self.options = options self.options = options
self.socketURL = socketURL self.socketURL = socketURL
@ -94,10 +102,8 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable
super.init() super.init()
} }
/** /// Not so type safe way to create a SocketIOClient, meant for Objective-C compatiblity.
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<SocketIOClientOption>)`
If using Swift it's recommended to use `init(socketURL: NSURL, options: Set<SocketIOClientOption>)`
*/
public convenience init(socketURL: NSURL, options: NSDictionary?) { public convenience init(socketURL: NSURL, options: NSDictionary?) {
self.init(socketURL: socketURL, options: options?.toSocketOptionsSet() ?? []) self.init(socketURL: socketURL, options: options?.toSocketOptionsSet() ?? [])
} }
@ -127,26 +133,17 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable
return engine! return engine!
} }
private func clearReconnectTimer() {
reconnectTimer?.invalidate()
reconnectTimer = nil
}
@available(*, deprecated=5.3, message="Please use disconnect()") @available(*, deprecated=5.3, message="Please use disconnect()")
public func close() { public func close() {
disconnect() disconnect()
} }
/** /// Connect to the server.
Connect to the server.
*/
public func connect() { public func connect() {
connect(timeoutAfter: 0, withTimeoutHandler: nil) 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)?) { public func connect(timeoutAfter timeoutAfter: Int, withTimeoutHandler handler: (() -> Void)?) {
assert(timeoutAfter >= 0, "Invalid timeout: \(timeoutAfter)") assert(timeoutAfter >= 0, "Invalid timeout: \(timeoutAfter)")
@ -203,8 +200,6 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable
func didConnect() { func didConnect() {
DefaultSocketLogger.Logger.log("Socket connected", type: logType) DefaultSocketLogger.Logger.log("Socket connected", type: logType)
status = .Connected status = .Connected
currentReconnectAttempt = 0
clearReconnectTimer()
// Don't handle as internal because something crazy could happen where // Don't handle as internal because something crazy could happen where
// we disconnect before it's handled // we disconnect before it's handled
@ -224,10 +219,8 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable
handleEvent("disconnect", data: [reason], isInternalMessage: true) handleEvent("disconnect", data: [reason], isInternalMessage: true)
} }
/** /// Disconnects the socket. Only reconnect the same socket if you know what you're doing.
Disconnects the socket. Only reconnect the same socket if you know what you're doing. /// Will turn off automatic reconnects.
Will turn off automatic reconnects.
*/
public func disconnect() { public func disconnect() {
DefaultSocketLogger.Logger.log("Closing socket", type: logType) DefaultSocketLogger.Logger.log("Closing socket", type: logType)
@ -235,16 +228,12 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable
didDisconnect("Disconnect") didDisconnect("Disconnect")
} }
/** /// Send a message to the server
Send a message to the server
*/
public func emit(event: String, _ items: AnyObject...) { public func emit(event: String, _ items: AnyObject...) {
emit(event, withItems: items) 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]) { public func emit(event: String, withItems items: [AnyObject]) {
guard status == .Connected else { guard status == .Connected else {
handleEvent("error", data: ["Tried emitting \(event) when not connected"], isInternalMessage: true) handleEvent("error", data: ["Tried emitting \(event) when not connected"], isInternalMessage: true)
@ -302,9 +291,14 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable
public func engineDidClose(reason: String) { public func engineDidClose(reason: String) {
waitingPackets.removeAll() waitingPackets.removeAll()
if status != .Closed {
status = .NotConnected
}
if status == .Closed || !reconnects { if status == .Closed || !reconnects {
didDisconnect(reason) didDisconnect(reason)
} else if status != .Reconnecting { } else if !reconnecting {
reconnecting = true
tryReconnectWithReason(reason) tryReconnectWithReason(reason)
} }
} }
@ -325,9 +319,7 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable
ackHandlers.executeAck(ack, items: data) 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) { public func handleEvent(event: String, data: [AnyObject], isInternalMessage: Bool, withAck ack: Int = -1) {
guard status == .Connected || isInternalMessage else { guard status == .Connected || isInternalMessage else {
return 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() { public func leaveNamespace() {
if nsp != "/" { if nsp != "/" {
engine?.send("1\(nsp)", withData: []) 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) { public func joinNamespace(namespace: String) {
nsp = namespace nsp = namespace
@ -366,28 +354,22 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable
} }
} }
/** /// Removes handler(s) based on name
Removes handler(s)
*/
public func off(event: String) { public func off(event: String) {
DefaultSocketLogger.Logger.log("Removing handler for event: %@", type: logType, args: event) DefaultSocketLogger.Logger.log("Removing handler for event: %@", type: logType, args: event)
handlers = handlers.filter { $0.event != 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) { public func off(id id: NSUUID) {
DefaultSocketLogger.Logger.log("Removing handler with id: %@", type: logType, args: id) DefaultSocketLogger.Logger.log("Removing handler with id: %@", type: logType, args: id)
handlers = handlers.filter { $0.id != id } handlers = handlers.filter { $0.id != id }
} }
/** /// Adds a handler for an event.
Adds a handler for an event. /// Returns: A unique id for the handler
Returns: A unique id for the handler
*/
public func on(event: String, callback: NormalCallback) -> NSUUID { public func on(event: String, callback: NormalCallback) -> NSUUID {
DefaultSocketLogger.Logger.log("Adding handler for event: %@", type: logType, args: event) 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 return handler.id
} }
/** /// Adds a single-use handler for an event.
Adds a single-use handler for an event. /// Returns: A unique id for the handler
Returns: A unique id for the handler
*/
public func once(event: String, callback: NormalCallback) -> NSUUID { public func once(event: String, callback: NormalCallback) -> NSUUID {
DefaultSocketLogger.Logger.log("Adding once handler for event: %@", type: logType, args: event) 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 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) { public func onAny(handler: (SocketAnyEvent) -> Void) {
anyHandler = handler anyHandler = handler
} }
@ -443,44 +421,34 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable
} }
} }
/** /// Tries to reconnect to the server.
Tries to reconnect to the server.
*/
public func reconnect() { public func reconnect() {
tryReconnectWithReason("manual reconnect") guard !reconnecting else { return }
engine?.disconnect("manual reconnect")
} }
/** /// Removes all handlers.
Removes all handlers. /// Can be used after disconnecting to break any potential remaining retain cycles.
Can be used after disconnecting to break any potential remaining retain cycles.
*/
public func removeAllHandlers() { public func removeAllHandlers() {
handlers.removeAll(keepCapacity: false) handlers.removeAll(keepCapacity: false)
} }
private func tryReconnectWithReason(reason: String) { private func tryReconnectWithReason(reason: String) {
if reconnectTimer == nil { if reconnecting {
DefaultSocketLogger.Logger.log("Starting reconnect", type: logType) DefaultSocketLogger.Logger.log("Starting reconnect", type: logType)
handleEvent("reconnect", data: [reason], isInternalMessage: true) handleEvent("reconnect", data: [reason], isInternalMessage: true)
status = .Reconnecting _tryReconnect()
dispatch_async(dispatch_get_main_queue()) {
self.reconnectTimer = NSTimer.scheduledTimerWithTimeInterval(Double(self.reconnectWait),
target: self, selector: "_tryReconnect", userInfo: nil, repeats: true)
}
} }
} }
@objc private func _tryReconnect() { @objc private func _tryReconnect() {
if status == .Connected { if !reconnecting {
clearReconnectTimer()
return return
} }
if reconnectAttempts != -1 && currentReconnectAttempt + 1 > reconnectAttempts || !reconnects { if reconnectAttempts != -1 && currentReconnectAttempt + 1 > reconnectAttempts || !reconnects {
clearReconnectTimer()
didDisconnect("Reconnect Failed") didDisconnect("Reconnect Failed")
return return
@ -492,6 +460,10 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable
currentReconnectAttempt += 1 currentReconnectAttempt += 1
connect() connect()
let dispatchAfter = dispatch_time(DISPATCH_TIME_NOW, Int64(UInt64(reconnectWait) * NSEC_PER_SEC))
dispatch_after(dispatchAfter, dispatch_get_main_queue(), _tryReconnect)
} }
} }

View File

@ -25,7 +25,7 @@
import Foundation import Foundation
@objc public enum SocketIOClientStatus: Int, CustomStringConvertible { @objc public enum SocketIOClientStatus: Int, CustomStringConvertible {
case NotConnected, Closed, Connecting, Connected, Reconnecting case NotConnected, Closed, Connecting, Connected
public var description: String { public var description: String {
switch self { switch self {
@ -37,8 +37,6 @@ import Foundation
return "Connecting" return "Connecting"
case Connected: case Connected:
return "Connected" return "Connected"
case Reconnecting:
return "Reconnecting"
} }
} }
} }