merge master

This commit is contained in:
Erik 2015-04-03 16:37:02 -04:00
commit a17f2b6e7a
6 changed files with 127 additions and 122 deletions

View File

@ -11,7 +11,7 @@ socket.on("connect") {data, ack in
socket.on("currentAmount") {data, ack in socket.on("currentAmount") {data, ack in
if let cur = data?[0] as? Double { if let cur = data?[0] as? Double {
socket.emitWithAck("canUpdate", cur).onAck(0) {data in socket.emitWithAck("canUpdate", cur)(timeout: 0) {data in
socket.emit("update", ["amount": cur + 2.50]) socket.emit("update", ["amount": cur + 2.50])
} }
@ -30,9 +30,9 @@ SocketIOClient* socket = [[SocketIOClient alloc] initWithSocketURL:@"localhost:8
[socket on: @"connect" callback: ^(NSArray* data, void (^ack)(NSArray*)) { [socket on: @"connect" callback: ^(NSArray* data, void (^ack)(NSArray*)) {
NSLog(@"connected"); NSLog(@"connected");
[socket emitObjc:@"echo" withItems:@[@"echo test"]]; [socket emitObjc:@"echo" withItems:@[@"echo test"]];
[[socket emitWithAckObjc:@"ackack" withItems:@[@"test"]] onAck:0 withCallback:^(NSArray* data) { [socket emitWithAckObjc:@"ackack" withItems:@[@1]](10, ^(NSArray* data) {
NSLog(@"Got data"); NSLog(@"Got ack");
}]; });
}]; }];
``` ```

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = "Socket.IO-Client-Swift" s.name = "Socket.IO-Client-Swift"
s.version = "1.4.4" s.version = "1.5.0"
s.summary = "Socket.IO-client for Swift" s.summary = "Socket.IO-client for Swift"
s.description = <<-DESC s.description = <<-DESC
Socket.IO-client for Swift. Socket.IO-client for Swift.
@ -12,7 +12,7 @@ Pod::Spec.new do |s|
s.author = { "Erik" => "nuclear.ace@gmail.com" } s.author = { "Erik" => "nuclear.ace@gmail.com" }
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.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v1.4.4' } s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v1.5.0' }
s.source_files = "SwiftIO/**/*.swift" s.source_files = "SwiftIO/**/*.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

@ -1,9 +1,9 @@
// //
// SocketAckHandler.swift // SocketAckMap.swift
// Socket.IO-Swift // SocketIO-Swift
//
// Created by Erik Little on 4/3/15.
// //
// Created by Erik Little on 2/14/15.
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
@ -25,45 +25,35 @@
import Foundation import Foundation
public typealias AckCallback = (NSArray?) -> Void public typealias AckCallback = (NSArray?) -> Void
public typealias OnAckCallback = (timeout:UInt64, callback:AckCallback) -> Void
@objc public class SocketAckHandler { struct SocketAckMap {
let ackNum:Int! private var acks = [Int: AckCallback]()
let event:String! private var waiting = [Int: Bool]()
var acked = false
var callback:AckCallback?
weak var socket:SocketIOClient?
mutating func addAck(ack:Int, callback:AckCallback) {
init(event:String, ackNum:Int = 0, socket:SocketIOClient) { waiting[ack] = true
self.ackNum = ackNum acks[ack] = callback
self.event = event
self.socket = socket
} }
public func onAck(timeout:UInt64, withCallback callback:AckCallback) { mutating func executeAck(ack:Int, items:[AnyObject]?) {
self.callback = callback let callback = acks[ack]
waiting[ack] = false
dispatch_async(dispatch_get_main_queue()) {
if timeout != 0 { callback?(items)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(timeout * NSEC_PER_SEC))
dispatch_after(time, dispatch_get_main_queue()) {[weak self] in
if self == nil {
return return
} }
if !self!.acked { acks.removeValueForKey(ack)
self?.executeAck(["No ACK"])
self?.socket?.removeAck(self!)
}
}
}
} }
func executeAck(data:NSArray?) { mutating func timeoutAck(ack:Int) {
dispatch_async(dispatch_get_main_queue()) {[weak self, cb = self.callback] in if waiting[ack] != nil && waiting[ack]! {
self?.acked = true acks[ack]?(["NO ACK"])
cb?(data)
return
} }
acks.removeValueForKey(ack)
waiting.removeValueForKey(ack)
} }
} }

View File

@ -175,6 +175,7 @@ public class SocketEngine: NSObject, WebSocketDelegate {
self._websocket = true self._websocket = true
self._polling = false self._polling = false
self.fastUpgrade = false self.fastUpgrade = false
self.probing = false
self.flushProbeWait() self.flushProbeWait()
} }
@ -229,7 +230,7 @@ public class SocketEngine: NSObject, WebSocketDelegate {
} }
private func flushProbeWait() { private func flushProbeWait() {
// println("flushing probe wait") // NSLog("flushing probe wait")
dispatch_async(self.emitQueue) {[weak self] in dispatch_async(self.emitQueue) {[weak self] in
if self == nil { if self == nil {
return return
@ -240,6 +241,10 @@ public class SocketEngine: NSObject, WebSocketDelegate {
} }
self?.probeWait.removeAll(keepCapacity: false) self?.probeWait.removeAll(keepCapacity: false)
if self?.postWait.count != 0 {
self?.flushWaitingForPostToWebSocket()
}
} }
} }
@ -269,11 +274,14 @@ public class SocketEngine: NSObject, WebSocketDelegate {
let postData = postStr.dataUsingEncoding(NSUTF8StringEncoding, let postData = postStr.dataUsingEncoding(NSUTF8StringEncoding,
allowLossyConversion: false)! allowLossyConversion: false)!
// NSLog("posting: \(postStr)")
req.HTTPBody = postData req.HTTPBody = postData
req.setValue(String(postData.length), forHTTPHeaderField: "Content-Length") req.setValue(String(postData.length), forHTTPHeaderField: "Content-Length")
self.waitingForPost = true self.waitingForPost = true
// NSLog("posting: \(postStr)")
// NSLog("Posting with WS status of: \(self.websocket)")
self.session.dataTaskWithRequest(req) {[weak self] data, res, err in self.session.dataTaskWithRequest(req) {[weak self] data, res, err in
if self == nil { if self == nil {
return return
@ -284,9 +292,13 @@ public class SocketEngine: NSObject, WebSocketDelegate {
self?.waitingForPost = false self?.waitingForPost = false
dispatch_async(self!.emitQueue) { dispatch_async(self!.emitQueue) {
if self!.fastUpgrade {
self?.doFastUpgrade()
return
} else {
self?.flushWaitingForPost() self?.flushWaitingForPost()
self?.doPoll() self?.doPoll()
return }
}}.resume() }}.resume()
} }
@ -518,6 +530,8 @@ public class SocketEngine: NSObject, WebSocketDelegate {
self.write("", withType: PacketType.PING, withData: nil) self.write("", withType: PacketType.PING, withData: nil)
} }
/// Send polling message.
/// Only call on emitQueue
private func sendPollMessage(var msg:String, withType type:PacketType, private func sendPollMessage(var msg:String, withType type:PacketType,
datas:ContiguousArray<NSData>? = nil) { datas:ContiguousArray<NSData>? = nil) {
// println("Sending poll: \(msg) as type: \(type.rawValue)") // println("Sending poll: \(msg) as type: \(type.rawValue)")
@ -539,6 +553,8 @@ public class SocketEngine: NSObject, WebSocketDelegate {
} }
} }
/// Send message on WebSockets
/// Only call on emitQueue
private func sendWebSocketMessage(str:String, withType type:PacketType, private func sendWebSocketMessage(str:String, withType type:PacketType,
datas:ContiguousArray<NSData>? = nil) { datas:ContiguousArray<NSData>? = nil) {
// println("Sending ws: \(str) as type: \(type.rawValue)") // println("Sending ws: \(str) as type: \(type.rawValue)")
@ -570,9 +586,9 @@ public class SocketEngine: NSObject, WebSocketDelegate {
private func upgradeTransport() { private func upgradeTransport() {
if self.websocketConnected { if self.websocketConnected {
// NSLog("Doing fast upgrade")
// Do a fast upgrade // Do a fast upgrade
self.fastUpgrade = true self.fastUpgrade = true
self.probing = false
self.sendPollMessage("", withType: PacketType.NOOP) self.sendPollMessage("", withType: PacketType.NOOP)
} }
} }

View File

@ -25,9 +25,7 @@
import Foundation import Foundation
public class SocketIOClient: NSObject, SocketEngineClient { public class SocketIOClient: NSObject, SocketEngineClient {
let reconnectAttempts:Int!
private lazy var params = [String: AnyObject]() private lazy var params = [String: AnyObject]()
private var ackHandlers = ContiguousArray<SocketAckHandler>()
private var anyHandler:((SocketAnyEvent) -> Void)? private var anyHandler:((SocketAnyEvent) -> Void)?
private var _closed = false private var _closed = false
private var _connected = false private var _connected = false
@ -42,14 +40,15 @@ public class SocketIOClient: NSObject, SocketEngineClient {
private var _reconnecting = false private var _reconnecting = false
private var reconnectTimer:NSTimer? private var reconnectTimer:NSTimer?
let reconnectAttempts:Int!
var ackHandlers = SocketAckMap()
var currentAck = -1 var currentAck = -1
var waitingData = ContiguousArray<SocketPacket>() var waitingData = ContiguousArray<SocketPacket>()
public let socketURL:String public let socketURL:String
public let handleQueue = dispatch_queue_create("handleQueue".cStringUsingEncoding(NSUTF8StringEncoding), public let handleAckQueue = dispatch_queue_create("handleAckQueue", DISPATCH_QUEUE_SERIAL)
DISPATCH_QUEUE_SERIAL) public let handleQueue = dispatch_queue_create("handleQueue", DISPATCH_QUEUE_SERIAL)
public let emitQueue = dispatch_queue_create("emitQueue".cStringUsingEncoding(NSUTF8StringEncoding), public let emitQueue = dispatch_queue_create("emitQueue", DISPATCH_QUEUE_SERIAL)
DISPATCH_QUEUE_SERIAL)
public var closed:Bool { public var closed:Bool {
return self._closed return self._closed
} }
@ -89,6 +88,26 @@ public class SocketIOClient: NSObject, SocketEngineClient {
// Set options // Set options
if opts != nil { if opts != nil {
if let cookies = opts!["cookies"] as? [NSHTTPCookie] {
self.cookies = cookies
}
if let polling = opts!["forcePolling"] as? Bool {
self.forcePolling = polling
}
if let ws = opts!["forceWebsockets"] as? Bool {
self.forceWebsockets = ws
}
if var nsp = opts!["nsp"] as? String {
if nsp != "/" && nsp.hasPrefix("/") {
nsp.removeAtIndex(nsp.startIndex)
}
self.nsp = nsp
}
if let reconnects = opts!["reconnects"] as? Bool { if let reconnects = opts!["reconnects"] as? Bool {
self.reconnects = reconnects self.reconnects = reconnects
} }
@ -102,26 +121,6 @@ public class SocketIOClient: NSObject, SocketEngineClient {
if let reconnectWait = opts!["reconnectWait"] as? Int { if let reconnectWait = opts!["reconnectWait"] as? Int {
self.reconnectWait = abs(reconnectWait) self.reconnectWait = abs(reconnectWait)
} }
if var nsp = opts!["nsp"] as? String {
if nsp != "/" && nsp.hasPrefix("/") {
nsp.removeAtIndex(nsp.startIndex)
}
self.nsp = nsp
}
if let polling = opts!["forcePolling"] as? Bool {
self.forcePolling = polling
}
if let ws = opts!["forceWebsockets"] as? Bool {
self.forceWebsockets = ws
}
if let cookies = opts!["cookies"] as? [NSHTTPCookie] {
self.cookies = cookies
}
} else { } else {
self.reconnectAttempts = -1 self.reconnectAttempts = -1
} }
@ -190,6 +189,29 @@ public class SocketIOClient: NSObject, SocketEngineClient {
self.engine?.open(opts: params) self.engine?.open(opts: params)
} }
private func createOnAck(event:String, items:[AnyObject]) -> OnAckCallback {
return {[weak self, ack = ++self.currentAck] timeout, callback in
if self == nil {
return
}
self?.ackHandlers.addAck(ack, callback)
dispatch_async(self!.emitQueue) {
self?._emit(event, items, ack: ack)
return
}
if timeout != 0 {
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(timeout * NSEC_PER_SEC))
dispatch_after(time, dispatch_get_main_queue()) {
self?.ackHandlers.timeoutAck(ack)
return
}
}
}
}
func didConnect() { func didConnect() {
self._closed = false self._closed = false
self._connected = true self._connected = true
@ -219,6 +241,13 @@ public class SocketIOClient: NSObject, SocketEngineClient {
self.handleEvent("disconnect", data: [reason], isInternalMessage: true) self.handleEvent("disconnect", data: [reason], isInternalMessage: true)
} }
/**
Same as close
*/
public func disconnect(#fast:Bool) {
self.close(fast: fast)
}
/** /**
Send a message to the server Send a message to the server
*/ */
@ -251,43 +280,23 @@ public class SocketIOClient: NSObject, SocketEngineClient {
Sends a message to the server, requesting an ack. Use the onAck method of SocketAckHandler to add Sends a message to the server, requesting an ack. Use the onAck method of SocketAckHandler to add
an ack. an ack.
*/ */
public func emitWithAck(event:String, _ items:AnyObject...) -> SocketAckHandler { public func emitWithAck(event:String, _ items:AnyObject...) -> OnAckCallback {
if !self.connected { if !self.connected {
return SocketAckHandler(event: "fail", socket: self) return createOnAck(event, items: items)
} }
self.currentAck++ return self.createOnAck(event, items: items)
let ackHandler = SocketAckHandler(event: event,
ackNum: self.currentAck, socket: self)
self.ackHandlers.append(ackHandler)
dispatch_async(self.emitQueue) {[weak self, ack = self.currentAck] in
self?._emit(event, items, ack: ack)
return
}
return ackHandler
} }
/** /**
Same as emitWithAck, but for Objective-C Same as emitWithAck, but for Objective-C
*/ */
public func emitWithAckObjc(event:String, withItems items:[AnyObject]) -> SocketAckHandler { public func emitWithAckObjc(event:String, withItems items:[AnyObject]) -> OnAckCallback {
if !self.connected { if !self.connected {
return SocketAckHandler(event: "fail", socket: self) return self.createOnAck(event, items: items)
} }
self.currentAck++ return self.createOnAck(event, items: items)
let ackHandler = SocketAckHandler(event: event,
ackNum: self.currentAck, socket: self)
self.ackHandlers.append(ackHandler)
dispatch_async(self.emitQueue) {[weak self, ack = self.currentAck] in
self?._emit(event, items, ack: ack)
return
}
return ackHandler
} }
private func _emit(event:String, _ args:[AnyObject], ack:Int? = nil) { private func _emit(event:String, _ args:[AnyObject], ack:Int? = nil) {
@ -331,21 +340,15 @@ public class SocketIOClient: NSObject, SocketEngineClient {
// Called when the socket gets an ack for something it sent // Called when the socket gets an ack for something it sent
func handleAck(ack:Int, data:AnyObject?) { func handleAck(ack:Int, data:AnyObject?) {
self.ackHandlers = self.ackHandlers.filter {handler in var ackData:[AnyObject]?
if handler.ackNum != ack {
return true
} else {
if data is NSArray { if data is NSArray {
handler.executeAck(data as? NSArray) ackData = data as? NSArray
} else if data != nil { } else if data != nil {
handler.executeAck([data!]) ackData = [data!]
} else {
handler.executeAck(nil)
} }
return false self.ackHandlers.executeAck(ack, items: ackData)
}
}
} }
/** /**
@ -428,10 +431,6 @@ public class SocketIOClient: NSObject, SocketEngineClient {
} }
} }
func removeAck(ack:SocketAckHandler) {
self.ackHandlers = self.ackHandlers.filter {$0 === ack ? false : true}
}
// We lost connection and should attempt to reestablish // We lost connection and should attempt to reestablish
func tryReconnect() { func tryReconnect() {
if self.reconnectAttempts != -1 && self.currentReconnectAttempt + 1 > self.reconnectAttempts { if self.reconnectAttempts != -1 && self.currentReconnectAttempt + 1 > self.reconnectAttempts {

View File

@ -24,7 +24,7 @@
import Foundation import Foundation
enum SocketPacketType: Int { enum SocketPacketType:Int {
case CONNECT = 0 case CONNECT = 0
case DISCONNECT = 1 case DISCONNECT = 1
case EVENT = 2 case EVENT = 2