Redo emitWithAck API since in Swift 3 parameter names aren't apart of the type

This commit is contained in:
Erik 2016-09-25 11:59:10 -04:00
parent 8ba3518fbc
commit 6771f59ab7
No known key found for this signature in database
GPG Key ID: 4930B7C5FBC1A69D
7 changed files with 66 additions and 52 deletions

View File

@ -6,6 +6,7 @@ Socket.IO-client for iOS/OS X.
##Example ##Example
```swift ```swift
import SocketIO import SocketIO
let socket = SocketIOClient(socketURL: URL(string: "http://localhost:8080")!, config: [.log(true), .forcePolling(true)]) let socket = SocketIOClient(socketURL: URL(string: "http://localhost:8080")!, config: [.log(true), .forcePolling(true)])
socket.on("connect") {data, ack in socket.on("connect") {data, ack in
@ -14,7 +15,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)(0) {data in socket.emitWithAck("canUpdate", cur).timingOut(after: 0) {data in
socket.emit("update", ["amount": cur + 2.50]) socket.emit("update", ["amount": cur + 2.50])
} }
@ -38,9 +39,9 @@ SocketIOClient* socket = [[SocketIOClient alloc] initWithSocketURL:url config:@{
[socket on:@"currentAmount" callback:^(NSArray* data, SocketAckEmitter* ack) { [socket on:@"currentAmount" callback:^(NSArray* data, SocketAckEmitter* ack) {
double cur = [[data objectAtIndex:0] floatValue]; double cur = [[data objectAtIndex:0] floatValue];
[socket emitWithAck:@"canUpdate" withItems:@[@(cur)]](0, ^(NSArray* data) { [[socket emitWithAck:@"canUpdate" withItems:@[@(cur)]] timingOutAfter:0 callback:^(NSArray* data) {
[socket emit:@"update" withItems:@[@{@"amount": @(cur + 2.50)}]]; [socket emit:@"update" withItems:@[@{@"amount": @(cur + 2.50)}]];
}); }];
[ack with:@[@"Got your currentAmount, ", @"dude"]]; [ack with:@[@"Got your currentAmount, ", @"dude"]];
}]; }];
@ -182,8 +183,8 @@ Methods
3. `onAny(callback:((event: String, items: AnyObject?)) -> Void)` - Adds a handler for all events. It will be called on any received event. 3. `onAny(callback:((event: String, items: AnyObject?)) -> Void)` - Adds a handler for all events. It will be called on any received event.
4. `emit(_ event: String, _ items: AnyObject...)` - Sends a message. Can send multiple items. 4. `emit(_ event: String, _ items: AnyObject...)` - Sends a message. Can send multiple items.
5. `emit(_ event: String, withItems items: [AnyObject])` - `emit` for Objective-C 5. `emit(_ event: String, withItems items: [AnyObject])` - `emit` for Objective-C
6. `emitWithAck(_ event: String, _ items: AnyObject...) -> (timeoutAfter: UInt64, callback: (NSArray?) -> Void) -> Void` - Sends a message that requests an acknowledgement from the server. Returns a function which you can use to add a handler. See example. Note: The message is not sent until you call the returned function. 6. `emitWithAck(_ event: String, _ items: AnyObject...) -> OnAckCallback` - Sends a message that requests an acknowledgement from the server. Returns an object which you can use to add a handler. See example. Note: The message is not sent until you call timingOut(after:) on the returned object.
7. `emitWithAck(_ event: String, withItems items: [AnyObject]) -> (UInt64, (NSArray?) -> Void) -> Void` - `emitWithAck` for Objective-C. Note: The message is not sent until you call the returned function. 7. `emitWithAck(_ event: String, withItems items: [AnyObject]) -> OnAckCallback` - `emitWithAck` for Objective-C. Note: The message is not sent until you call timingOutAfter on the returned object.
8. `connect()` - Establishes a connection to the server. A "connect" event is fired upon successful connection. 8. `connect()` - Establishes a connection to the server. A "connect" event is fired upon successful connection.
9. `connect(timeoutAfter timeoutAfter: Int, withTimeoutHandler handler: (() -> Void)?)` - Connect to the server. If it isn't connected after timeoutAfter seconds, the handler is called. 9. `connect(timeoutAfter timeoutAfter: Int, withTimeoutHandler handler: (() -> Void)?)` - Connect to the server. If it isn't connected after timeoutAfter seconds, the handler is called.
10. `disconnect()` - Closes the socket. Reopening a disconnected socket is not fully tested. 10. `disconnect()` - Closes the socket. Reopening a disconnected socket is not fully tested.

View File

@ -35,9 +35,9 @@
} }
- (void)testEmitWithAckSyntax { - (void)testEmitWithAckSyntax {
[self.socket emitWithAck:@"testAckEmit" with:@[@YES]](0, ^(NSArray* data) { [[self.socket emitWithAck:@"testAckEmit" with:@[@YES]] timingOutAfter:0 callback:^(NSArray* data) {
}); }];
} }
- (void)testOffSyntax { - (void)testOffSyntax {

View File

@ -25,20 +25,20 @@ class SocketSideEffectTest: XCTestCase {
} }
func testFirstAck() { func testFirstAck() {
socket.emitWithAck("test")(0) {data in} socket.emitWithAck("test").timingOut(after: 0) {data in}
XCTAssertEqual(socket.currentAck, 0) XCTAssertEqual(socket.currentAck, 0)
} }
func testSecondAck() { func testSecondAck() {
socket.emitWithAck("test")(0) {data in} socket.emitWithAck("test").timingOut(after: 0) {data in}
socket.emitWithAck("test")(0) {data in} socket.emitWithAck("test").timingOut(after: 0) {data in}
XCTAssertEqual(socket.currentAck, 1) XCTAssertEqual(socket.currentAck, 1)
} }
func testHandleAck() { func testHandleAck() {
let expect = expectation(description: "handled ack") let expect = expectation(description: "handled ack")
socket.emitWithAck("test")(0) {data in socket.emitWithAck("test").timingOut(after: 0) {data in
XCTAssertEqual(data[0] as? String, "hello world") XCTAssertEqual(data[0] as? String, "hello world")
expect.fulfill() expect.fulfill()
} }
@ -49,7 +49,7 @@ class SocketSideEffectTest: XCTestCase {
func testHandleAck2() { func testHandleAck2() {
let expect = expectation(description: "handled ack2") let expect = expectation(description: "handled ack2")
socket.emitWithAck("test")(0) {data in socket.emitWithAck("test").timingOut(after: 0) {data in
XCTAssertTrue(data.count == 2, "Wrong number of ack items") XCTAssertTrue(data.count == 2, "Wrong number of ack items")
expect.fulfill() expect.fulfill()
} }

View File

@ -45,3 +45,39 @@ public final class SocketAckEmitter : NSObject {
socket.emitAck(ackNum, with: items) socket.emitAck(ackNum, with: items)
} }
} }
public final class OnAckCallback : NSObject {
private let ackNumber: Int
private let items: [Any]
private weak var socket: SocketIOClient?
init(ackNumber: Int, items: [Any], socket: SocketIOClient) {
self.ackNumber = ackNumber
self.items = items
self.socket = socket
}
deinit {
DefaultSocketLogger.Logger.log("OnAckCallback for \(ackNumber) being released", type: "OnAckCallback")
}
public func timingOut(after seconds: Int, callback: @escaping AckCallback) {
guard let socket = self.socket else { return }
socket.ackQueue.sync() {
socket.ackHandlers.addAck(ackNumber, callback: callback)
}
socket._emit(items, ack: ackNumber)
guard seconds != 0 else { return }
let time = DispatchTime.now() + Double(Int64(UInt64(seconds) * NSEC_PER_SEC)) / Double(NSEC_PER_SEC)
socket.handleQueue.asyncAfter(deadline: time) {
socket.ackHandlers.timeoutAck(self.ackNumber, onQueue: socket.handleQueue)
}
}
}

View File

@ -46,21 +46,22 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
public var reconnects = true public var reconnects = true
public var reconnectWait = 10 public var reconnectWait = 10
private let ackQueue = DispatchQueue(label: "com.socketio.ackQueue", attributes: [])
private let emitQueue = DispatchQueue(label: "com.socketio.emitQueue", attributes: [])
private let logType = "SocketIOClient" private let logType = "SocketIOClient"
private let parseQueue = DispatchQueue(label: "com.socketio.parseQueue", attributes: []) private let parseQueue = DispatchQueue(label: "com.socketio.parseQueue")
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 ackHandlers = SocketAckManager()
private var reconnecting = false private var reconnecting = false
private(set) var currentAck = -1 private(set) var currentAck = -1
private(set) var handleQueue = DispatchQueue.main private(set) var handleQueue = DispatchQueue.main
private(set) var reconnectAttempts = -1 private(set) var reconnectAttempts = -1
let ackQueue = DispatchQueue(label: "com.socketio.ackQueue")
let emitQueue = DispatchQueue(label: "com.socketio.emitQueue")
var ackHandlers = SocketAckManager()
var waitingPackets = [SocketPacket]() var waitingPackets = [SocketPacket]()
public var sid: String? { public var sid: String? {
@ -163,24 +164,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
private func createOnAck(_ items: [Any]) -> OnAckCallback { private func createOnAck(_ items: [Any]) -> OnAckCallback {
currentAck += 1 currentAck += 1
return {[weak self, ack = currentAck] timeout, callback in return OnAckCallback(ackNumber: currentAck, items: items, socket: self)
guard let this = self else { return }
this.ackQueue.sync() {
this.ackHandlers.addAck(ack, callback: callback)
}
this._emit(items, ack: ack)
if timeout != 0 {
let time = DispatchTime.now() + Double(Int64(timeout * NSEC_PER_SEC)) / Double(NSEC_PER_SEC)
this.handleQueue.asyncAfter(deadline: time) {
this.ackHandlers.timeoutAck(ack, onQueue: this.handleQueue)
}
}
}
} }
func didConnect() { func didConnect() {
@ -238,7 +222,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
return createOnAck([event] + items) return createOnAck([event] + items)
} }
private func _emit(_ data: [Any], ack: Int? = nil) { func _emit(_ data: [Any], ack: Int? = nil) {
emitQueue.async { emitQueue.async {
guard self.status == .connected else { guard self.status == .connected else {
self.handleEvent("error", data: ["Tried emitting when not connected"], isInternalMessage: true) self.handleEvent("error", data: ["Tried emitting when not connected"], isInternalMessage: true)

View File

@ -87,25 +87,18 @@ struct SocketPacket {
} }
private func completeMessage(_ message: String) -> String { private func completeMessage(_ message: String) -> String {
let restOfMessage: String
if data.count == 0 { if data.count == 0 {
return message + "[]" return message + "[]"
} }
do { guard let jsonSend = try? data.toJSON(), let jsonString = String(data: jsonSend, encoding: .utf8) else {
let jsonSend = try data.toJSON()
guard let jsonString = String(data: jsonSend, encoding: .utf8) else { return message + "[]" }
restOfMessage = jsonString
} catch {
DefaultSocketLogger.Logger.error("Error creating JSON object in SocketPacket.completeMessage", DefaultSocketLogger.Logger.error("Error creating JSON object in SocketPacket.completeMessage",
type: SocketPacket.logType) type: SocketPacket.logType)
restOfMessage = "[]" return message + "[]"
} }
return message + restOfMessage return message + jsonString
} }
private func createPacketString() -> String { private func createPacketString() -> String {
@ -133,11 +126,11 @@ struct SocketPacket {
// binary data // binary data
private func _fillInPlaceholders(_ object: Any) -> Any { private func _fillInPlaceholders(_ object: Any) -> Any {
switch object { switch object {
case let dict as [String: Any]: case let dict as JSON:
if dict["_placeholder"] as? Bool ?? false { if dict["_placeholder"] as? Bool ?? false {
return binary[dict["num"] as! Int] return binary[dict["num"] as! Int]
} else { } else {
return dict.reduce([String: Any](), {cur, keyValue in return dict.reduce(JSON(), {cur, keyValue in
var cur = cur var cur = cur
cur[keyValue.0] = _fillInPlaceholders(keyValue.1) cur[keyValue.0] = _fillInPlaceholders(keyValue.1)
@ -181,7 +174,7 @@ extension SocketPacket {
private extension SocketPacket { private extension SocketPacket {
// Recursive function that looks for NSData in collections // Recursive function that looks for NSData in collections
static func shred(_ data: Any, binary: inout [Data]) -> Any { static func shred(_ data: Any, binary: inout [Data]) -> Any {
let placeholder = ["_placeholder": true, "num": binary.count] as [String : Any] let placeholder = ["_placeholder": true, "num": binary.count] as JSON
switch data { switch data {
case let bin as Data: case let bin as Data:
@ -190,8 +183,8 @@ private extension SocketPacket {
return placeholder return placeholder
case let arr as [Any]: case let arr as [Any]:
return arr.map({shred($0, binary: &binary)}) return arr.map({shred($0, binary: &binary)})
case let dict as [String: Any]: case let dict as JSON:
return dict.reduce([String: Any](), {cur, keyValue in return dict.reduce(JSON(), {cur, keyValue in
var mutCur = cur var mutCur = cur
mutCur[keyValue.0] = shred(keyValue.1, binary: &binary) mutCur[keyValue.0] = shred(keyValue.1, binary: &binary)

View File

@ -41,8 +41,8 @@ extension String : SocketData {}
public typealias AckCallback = ([Any]) -> Void public typealias AckCallback = ([Any]) -> Void
public typealias NormalCallback = ([Any], SocketAckEmitter) -> Void public typealias NormalCallback = ([Any], SocketAckEmitter) -> Void
public typealias OnAckCallback = (_ timeoutAfter: UInt64, _ callback: @escaping AckCallback) -> Void
typealias JSON = [String: Any]
typealias Probe = (msg: String, type: SocketEnginePacketType, data: [Data]) typealias Probe = (msg: String, type: SocketEnginePacketType, data: [Data])
typealias ProbeWaitQueue = [Probe] typealias ProbeWaitQueue = [Probe]