diff --git a/SocketIO-MacTests/SocketAckManagerTest.swift b/SocketIO-MacTests/SocketAckManagerTest.swift index 67315ac..0515625 100644 --- a/SocketIO-MacTests/SocketAckManagerTest.swift +++ b/SocketIO-MacTests/SocketAckManagerTest.swift @@ -13,14 +13,14 @@ class SocketAckManagerTest: XCTestCase { var ackManager = SocketAckManager() func testAddAcks() { - let callbackExpection = self.expectationWithDescription("callbackExpection") + let callbackExpection = self.expectation(withDescription: "callbackExpection") let itemsArray = ["Hi", "ho"] func callback(items: [AnyObject]) { callbackExpection.fulfill() } ackManager.addAck(1, callback: callback) ackManager.executeAck(1, items: itemsArray) - waitForExpectationsWithTimeout(3.0, handler: nil) + waitForExpectations(withTimeout: 3.0, handler: nil) } } diff --git a/SocketIO-MacTests/SocketBasicPacketTest.swift b/SocketIO-MacTests/SocketBasicPacketTest.swift index b4b112d..d9ed590 100644 --- a/SocketIO-MacTests/SocketBasicPacketTest.swift +++ b/SocketIO-MacTests/SocketBasicPacketTest.swift @@ -10,8 +10,8 @@ import XCTest @testable import SocketIOClientSwift class SocketBasicPacketTest: XCTestCase { - let data = "test".dataUsingEncoding(NSUTF8StringEncoding)! - let data2 = "test2".dataUsingEncoding(NSUTF8StringEncoding)! + let data = "test".data(usingEncoding: NSUTF8StringEncoding)! + let data2 = "test2".data(usingEncoding: NSUTF8StringEncoding)! func testEmpyEmit() { let expectedSendString = "2[\"test\"]" diff --git a/SocketIO-MacTests/SocketEngineTest.swift b/SocketIO-MacTests/SocketEngineTest.swift index 7b2388c..24f46e0 100644 --- a/SocketIO-MacTests/SocketEngineTest.swift +++ b/SocketIO-MacTests/SocketEngineTest.swift @@ -22,17 +22,17 @@ class SocketEngineTest: XCTestCase { } func testBasicPollingMessage() { - let expectation = expectationWithDescription("Basic polling test") + let expect = expectation(withDescription: "Basic polling test") client.on("blankTest") {data, ack in - expectation.fulfill() + expect.fulfill() } engine.parsePollingMessage("15:42[\"blankTest\"]") - waitForExpectationsWithTimeout(3, handler: nil) + waitForExpectations(withTimeout: 3, handler: nil) } func testTwoPacketsInOnePollTest() { - let finalExpectation = expectationWithDescription("Final packet in poll test") + let finalExpectation = expectation(withDescription: "Final packet in poll test") var gotBlank = false client.on("blankTest") {data, ack in @@ -48,11 +48,11 @@ class SocketEngineTest: XCTestCase { } engine.parsePollingMessage("15:42[\"blankTest\"]24:42[\"stringTest\",\"hello\"]") - waitForExpectationsWithTimeout(3, handler: nil) + waitForExpectations(withTimeout: 3, handler: nil) } func testEngineDoesErrorOnUnknownTransport() { - let finalExpectation = expectationWithDescription("Unknown Transport") + let finalExpectation = expectation(withDescription: "Unknown Transport") client.on("error") {data, ack in if let error = data[0] as? String where error == "Unknown transport" { @@ -61,29 +61,29 @@ class SocketEngineTest: XCTestCase { } engine.parseEngineMessage("{\"code\": 0, \"message\": \"Unknown transport\"}", fromPolling: false) - waitForExpectationsWithTimeout(3, handler: nil) + waitForExpectations(withTimeout: 3, handler: nil) } func testEngineDoesErrorOnUnknownMessage() { - let finalExpectation = expectationWithDescription("Engine Errors") + let finalExpectation = expectation(withDescription: "Engine Errors") client.on("error") {data, ack in finalExpectation.fulfill() } engine.parseEngineMessage("afafafda", fromPolling: false) - waitForExpectationsWithTimeout(3, handler: nil) + waitForExpectations(withTimeout: 3, handler: nil) } func testEngineDecodesUTF8Properly() { - let expectation = expectationWithDescription("Engine Decodes utf8") + let expect = expectation(withDescription: "Engine Decodes utf8") client.on("stringTest") {data, ack in XCTAssertEqual(data[0] as? String, "lïne one\nlīne \rtwo", "Failed string test") - expectation.fulfill() + expect.fulfill() } engine.parsePollingMessage("41:42[\"stringTest\",\"lïne one\\nlÄ«ne \\rtwo\"]") - waitForExpectationsWithTimeout(3, handler: nil) + waitForExpectations(withTimeout: 3, handler: nil) } } diff --git a/SocketIO-MacTests/SocketNamespacePacketTest.swift b/SocketIO-MacTests/SocketNamespacePacketTest.swift index 4a235ba..54f72e9 100644 --- a/SocketIO-MacTests/SocketNamespacePacketTest.swift +++ b/SocketIO-MacTests/SocketNamespacePacketTest.swift @@ -10,8 +10,8 @@ import XCTest @testable import SocketIOClientSwift class SocketNamespacePacketTest: XCTestCase { - let data = "test".dataUsingEncoding(NSUTF8StringEncoding)! - let data2 = "test2".dataUsingEncoding(NSUTF8StringEncoding)! + let data = "test".data(usingEncoding: NSUTF8StringEncoding)! + let data2 = "test2".data(usingEncoding: NSUTF8StringEncoding)! func testEmpyEmit() { let expectedSendString = "2/swift,[\"test\"]" diff --git a/SocketIO-MacTests/SocketObjectiveCTest.m b/SocketIO-MacTests/SocketObjectiveCTest.m index 8372112..6fbbcbe 100644 --- a/SocketIO-MacTests/SocketObjectiveCTest.m +++ b/SocketIO-MacTests/SocketObjectiveCTest.m @@ -31,11 +31,11 @@ } - (void)testEmitSyntax { - [self.socket emit:@"testEmit" withItems:@[@YES]]; + [self.socket emit:@"testEmit" with:@[@YES]]; } - (void)testEmitWithAckSyntax { - [self.socket emitWithAck:@"testAckEmit" withItems:@[@YES]]; + [self.socket emitWithAck:@"testAckEmit" with:@[@YES]]; } - (void)testOffSyntax { diff --git a/SocketIO-MacTests/SocketParserTest.swift b/SocketIO-MacTests/SocketParserTest.swift index 9513fcd..7cbe982 100644 --- a/SocketIO-MacTests/SocketParserTest.swift +++ b/SocketIO-MacTests/SocketParserTest.swift @@ -25,6 +25,7 @@ class SocketParserTest: XCTestCase { "0/swift": ("/swift", [], [], -1), "1/swift": ("/swift", [], [], -1), "4\"ERROR\"": ("/", ["ERROR"], [], -1), + "4{\"test\":2}": ("/", [["test": 2]], [], -1), "41": ("/", [1], [], -1)] func testDisconnect() { @@ -87,6 +88,11 @@ class SocketParserTest: XCTestCase { validateParseResult(message) } + func testErrorTypeDictionary() { + let message = "4{\"test\":2}" + validateParseResult(message) + } + func testErrorTypeInt() { let message = "41" validateParseResult(message) @@ -113,12 +119,12 @@ class SocketParserTest: XCTestCase { func validateParseResult(message: String) { let validValues = SocketParserTest.packetTypes[message]! let packet = testSocket.parseString(message) - let type = message.substringWithRange(Range(message.startIndex..(message.startIndex.. OnAckCallback { return emitWithAck(event, with: items) } - /** - Same as emitWithAck, but for Objective-C - */ + /// Same as emitWithAck, but for Objective-C public func emitWithAck(event: String, with items: [AnyObject]) -> OnAckCallback { return createOnAck([event] + items) } private func _emit(data: [AnyObject], ack: Int? = nil) { - guard status == .connected else { - handleEvent("error", data: ["Tried emitting when not connected"], isInternalMessage: true) - return + dispatch_async(emitQueue) { + guard self.status == .connected else { + self.handleEvent("error", data: ["Tried emitting when not connected"], isInternalMessage: true) + return + } + + let packet = SocketPacket.packetFromEmit(data, id: ack ?? -1, nsp: self.nsp, ack: false) + let str = packet.packetString + + DefaultSocketLogger.Logger.log("Emitting: %@", type: self.logType, args: str) + + self.engine?.send(str, withData: packet.binary) } - - let packet = SocketPacket.packetFromEmit(data, id: ack ?? -1, nsp: nsp, ack: false) - let str = packet.packetString - - DefaultSocketLogger.Logger.log("Emitting: %@", type: logType, args: str) - - engine?.send(str, withData: packet.binary) } // If the server wants to know that the client received data @@ -295,7 +288,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable // Called when the socket gets an ack for something it sent func handleAck(ack: Int, data: [AnyObject]) { - guard status == .connected else {return} + guard status == .connected else { return } DefaultSocketLogger.Logger.log("Handling ack: %@ with data: %@", type: logType, args: ack, data ?? "") @@ -427,9 +420,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable } if reconnectAttempts != -1 && currentReconnectAttempt + 1 > reconnectAttempts || !reconnects { - didDisconnect("Reconnect Failed") - - return + return didDisconnect("Reconnect Failed") } DefaultSocketLogger.Logger.log("Trying to reconnect", type: logType) diff --git a/Source/SocketParsable.swift b/Source/SocketParsable.swift index 8e802a8..6926a8d 100644 --- a/Source/SocketParsable.swift +++ b/Source/SocketParsable.swift @@ -76,7 +76,7 @@ extension SocketParsable { return .Right(SocketPacket(type: type, nsp: "/")) } - var namespace: String? + var namespace = "/" var placeholders = -1 if type == .BinaryEvent || type == .BinaryAck { @@ -116,16 +116,16 @@ extension SocketParsable { switch parseData(noPlaceholders) { case let .Left(err): - // If first you don't succeed, try again + // Errors aren't always enclosed in an array if case let .Right(data) = parseData("\([noPlaceholders as AnyObject])") { return .Right(SocketPacket(type: type, data: data, id: Int(idString) ?? -1, - nsp: namespace ?? "/", placeholders: placeholders)) + nsp: namespace, placeholders: placeholders)) } else { return .Left(err) } case let .Right(data): return .Right(SocketPacket(type: type, data: data, id: Int(idString) ?? -1, - nsp: namespace ?? "/", placeholders: placeholders)) + nsp: namespace, placeholders: placeholders)) } } diff --git a/Source/WebSocket.swift b/Source/WebSocket.swift index a440c80..ed3642c 100644 --- a/Source/WebSocket.swift +++ b/Source/WebSocket.swift @@ -183,30 +183,42 @@ public class WebSocket : NSObject, NSStreamDelegate { } } - ///write a string to the websocket. This sends it as a text frame. - public func writeString(str: String) { + /** + Write a string to the websocket. This sends it as a text frame. + + If you supply a non-nil completion block, I will perform it when the write completes. + - parameter str: The string to write. + - parameter completion: The (optional) completion handler. + */ + public func writeString(str: String, completion: (() -> ())? = nil) { guard isConnected else { return } - dequeueWrite(str.data(usingEncoding: NSUTF8StringEncoding)!, code: .TextFrame) + dequeueWrite(str.data(usingEncoding: NSUTF8StringEncoding)!, code: .TextFrame, writeCompletion: completion) } - ///write binary data to the websocket. This sends it as a binary frame. - public func writeData(data: NSData) { + /** + Write binary data to the websocket. This sends it as a binary frame. + + If you supply a non-nil completion block, I will perform it when the write completes. + - parameter data: The data to write. + - parameter completion: The (optional) completion handler. + */ + public func writeData(data: NSData, completion: (() -> ())? = nil) { guard isConnected else { return } - dequeueWrite(data, code: .BinaryFrame) + dequeueWrite(data, code: .BinaryFrame, writeCompletion: completion) } //write a ping to the websocket. This sends it as a control frame. //yodel a sound to the planet. This sends it as an astroid. http://youtu.be/Eu5ZJELRiJ8?t=42s - public func writePing(data: NSData) { + public func writePing(data: NSData, completion: (() -> ())? = nil) { guard isConnected else { return } - dequeueWrite(data, code: .Ping) + dequeueWrite(data, code: .Ping, writeCompletion: completion) } //private method that starts the connection private func createHTTPRequest() { let urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, "GET", - url, kCFHTTPVersion1_1).takeRetainedValue() + url, kCFHTTPVersion1_1).takeRetainedValue() var port = url.port if port == nil { @@ -288,18 +300,18 @@ public class WebSocket : NSObject, NSStreamDelegate { if let cipherSuites = self.enabledSSLCipherSuites { if let sslContextIn = CFReadStreamCopyProperty(inputStream, kCFStreamPropertySSLContext) as! SSLContextRef?, sslContextOut = CFWriteStreamCopyProperty(outputStream, kCFStreamPropertySSLContext) as! SSLContextRef? { - let resIn = SSLSetEnabledCiphers(sslContextIn, cipherSuites, cipherSuites.count) - let resOut = SSLSetEnabledCiphers(sslContextOut, cipherSuites, cipherSuites.count) - if resIn != errSecSuccess { - let error = self.errorWithDetail("Error setting ingoing cypher suites", code: UInt16(resIn)) - disconnectStream(error) - return - } - if resOut != errSecSuccess { - let error = self.errorWithDetail("Error setting outgoing cypher suites", code: UInt16(resOut)) - disconnectStream(error) - return - } + let resIn = SSLSetEnabledCiphers(sslContextIn, cipherSuites, cipherSuites.count) + let resOut = SSLSetEnabledCiphers(sslContextOut, cipherSuites, cipherSuites.count) + if resIn != errSecSuccess { + let error = self.errorWithDetail("Error setting ingoing cypher suites", code: UInt16(resIn)) + disconnectStream(error) + return + } + if resOut != errSecSuccess { + let error = self.errorWithDetail("Error setting outgoing cypher suites", code: UInt16(resOut)) + disconnectStream(error) + return + } } } #endif @@ -436,7 +448,7 @@ public class WebSocket : NSObject, NSStreamDelegate { } case -1: fragBuffer = NSData(bytes: buffer, length: bufferLen) - break //do nothing, we are going to collect more data + break //do nothing, we are going to collect more data default: doDisconnect(errorWithDetail("Invalid HTTP upgrade", code: UInt16(code))) } @@ -555,10 +567,10 @@ public class WebSocket : NSObject, NSStreamDelegate { let isControlFrame = (receivedOpcode == .ConnectionClose || receivedOpcode == .Ping) if !isControlFrame && (receivedOpcode != .BinaryFrame && receivedOpcode != .ContinueFrame && receivedOpcode != .TextFrame && receivedOpcode != .Pong) { - let errCode = CloseCode.ProtocolError.rawValue - doDisconnect(errorWithDetail("unknown opcode: \(receivedOpcode)", code: errCode)) - writeError(errCode) - return + let errCode = CloseCode.ProtocolError.rawValue + doDisconnect(errorWithDetail("unknown opcode: \(receivedOpcode)", code: errCode)) + writeError(errCode) + return } if isControlFrame && isFin == 0 { let errCode = CloseCode.ProtocolError.rawValue @@ -611,7 +623,7 @@ public class WebSocket : NSObject, NSStreamDelegate { if dataLength > UInt64(bufferLen) { len = UInt64(bufferLen-offset) } - var data: NSData! + let data: NSData if len < 0 { len = 0 data = NSData() @@ -747,7 +759,7 @@ public class WebSocket : NSObject, NSStreamDelegate { dequeueWrite(NSData(bytes: buffer, length: sizeof(UInt16)), code: .ConnectionClose) } ///used to write things to the stream - private func dequeueWrite(data: NSData, code: OpCode) { + private func dequeueWrite(data: NSData, code: OpCode, writeCompletion: (() -> ())? = nil) { writeQueue.addOperation { [weak self] in //stream isn't ready, let's wait guard let s = self else { return } @@ -796,6 +808,12 @@ public class WebSocket : NSObject, NSStreamDelegate { total += len } if total >= offset { + if let queue = self?.queue, callback = writeCompletion { + dispatch_async(queue) { + callback() + } + } + break } }