diff --git a/CHANGELOG.md b/CHANGELOG.md index e18653c..b09d4e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to this project will be documented in this file. `Starscream` adheres to [Semantic Versioning](http://semver.org/). +#### [2.0.0](https://github.com/daltoniam/Starscream/tree/2.0.0) + +Added Swift 3 support. + +Fixed: +[#229](https://github.com/daltoniam/Starscream/issues/229) +[#232](https://github.com/daltoniam/Starscream/issues/232) + #### [1.1.3](https://github.com/daltoniam/Starscream/tree/1.1.3) Changed: @@ -47,4 +55,4 @@ Fixes for #121, #123 #### [1.0.0](https://github.com/daltoniam/Starscream/tree/1.0.0) -first release of Swift 2 support. \ No newline at end of file +first release of Swift 2 support. diff --git a/LICENSE b/LICENSE index f1a5930..d6ab2f1 100644 --- a/LICENSE +++ b/LICENSE @@ -2,7 +2,7 @@ Version 2.0, January 2004 http://www.apache.org/licenses/ - Copyright (c) 2014-2015 Dalton Cherry. + Copyright (c) 2014-2016 Dalton Cherry. TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION diff --git a/Package.swift b/Package.swift index c2b7940..163515b 100644 --- a/Package.swift +++ b/Package.swift @@ -3,7 +3,7 @@ // Starscream // // Created by Dalton Cherry on 5/16/15. -// Copyright (c) 2014-2015 Dalton Cherry. +// Copyright (c) 2014-2016 Dalton Cherry. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -22,4 +22,4 @@ import PackageDescription let package = Package( name: "Starscream" -) \ No newline at end of file +) diff --git a/README.md b/README.md index dc5afe1..a105ec2 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,6 @@ Starscream is a conforming WebSocket ([RFC 6455](http://tools.ietf.org/html/rfc6 It's Objective-C counter part can be found here: [Jetfire](https://github.com/acmacalister/jetfire) -###Swift 3/Xcode 8 -If you are looking for Swift 3 support, see [swift 3 here](https://github.com/daltoniam/Starscream/tree/swift3) - ## Features - Conforms to all of the base [Autobahn test suite](http://autobahn.ws/testsuite/). @@ -25,7 +22,7 @@ import Starscream Once imported, you can open a connection to your WebSocket server. Note that `socket` is probably best as a property, so it doesn't get deallocated right after being setup. ```swift -socket = WebSocket(url: NSURL(string: "ws://localhost:8080/")!) +socket = WebSocket(url: URL(string: "ws://localhost:8080/")!) socket.delegate = self socket.connect() ``` @@ -67,8 +64,8 @@ func websocketDidReceiveMessage(socket: WebSocket, text: String) { websocketDidReceiveData is called when the client gets a binary frame from the connection. ```swift -func websocketDidReceiveData(socket: WebSocket, data: NSData) { - print("got some data: \(data.length)") +func websocketDidReceiveData(socket: WebSocket, data: Data) { + print("got some data: \(data.count)") } ``` @@ -77,15 +74,15 @@ func websocketDidReceiveData(socket: WebSocket, data: NSData) { websocketDidReceivePong is called when the client gets a pong response from the connection. You need to implement the WebSocketPongDelegate protocol and set an additional delegate, eg: ` socket.pongDelegate = self` ```swift -func websocketDidReceivePong(socket: WebSocket) { - print("Got pong!") +func websocketDidReceivePong(socket: WebSocket, data: Data?) { + print("Got pong! Maybe some data: \(data?.count)") } ``` Or you can use closures. ```swift -socket = WebSocket(url: NSURL(string: "ws://localhost:8080/")!) +socket = WebSocket(url: URL(string: "ws://localhost:8080/")!) //websocketDidConnect socket.onConnect = { print("websocket is connected") @@ -99,8 +96,8 @@ socket.onText = { (text: String) in print("got some text: \(text)") } //websocketDidReceiveData -socket.onData = { (data: NSData) in - print("got some data: \(data.length)") +socket.onData = { (data: Data) in + print("got some data: \(data.count)") } //you could do onPong as well. socket.connect() @@ -111,28 +108,28 @@ One more: you can listen to socket connection and disconnection via notification ## The delegate methods give you a simple way to handle data from the server, but how do you send data? -### writeData +### write a binary frame -The writeData method gives you a simple way to send `NSData` (binary) data to the server. +The writeData method gives you a simple way to send `Data` (binary) data to the server. ```swift -socket.writeData(data) //write some NSData over the socket! +socket.write(data: data) //write some Data over the socket! ``` -### writeString +### write a string frame The writeString method is the same as writeData, but sends text/string. ```swift -socket.writeString("Hi Server!") //example on how to write text over the socket! +socket.write(string: "Hi Server!") //example on how to write text over the socket! ``` -### writePing +### write a ping frame -The writePing method is the same as writeData, but sends a ping control frame. +The writePing method is the same as write, but sends a ping control frame. ```swift -socket.writePing(NSData()) //example on how to write a ping control frame over the socket! +socket.write(ping: Data()) //example on how to write a ping control frame over the socket! ``` ### disconnect @@ -169,7 +166,7 @@ If you need to specify a protocol, simple add it to the init: ```swift //chat and superchat are the example protocols here -socket = WebSocket(url: NSURL(string: "ws://localhost:8080/")!, protocols: ["chat","superchat"]) +socket = WebSocket(url: URL(string: "ws://localhost:8080/")!, protocols: ["chat","superchat"]) socket.delegate = self socket.connect() ``` @@ -179,13 +176,13 @@ socket.connect() There are a couple of other properties that modify the stream: ```swift -socket = WebSocket(url: NSURL(string: "ws://localhost:8080/")!, protocols: ["chat","superchat"]) +socket = WebSocket(url: URL(string: "ws://localhost:8080/")!, protocols: ["chat","superchat"]) //set this if you are planning on using the socket in a VOIP background setting (using the background VOIP service). socket.voipEnabled = true //set this you want to ignore SSL cert validation, so a self signed SSL certificate can be used. -socket.selfSignedSSL = true +socket.disableSSLCertValidation = true ``` ### SSL Pinning @@ -193,21 +190,21 @@ socket.selfSignedSSL = true SSL Pinning is also supported in Starscream. ```swift -socket = WebSocket(url: NSURL(string: "ws://localhost:8080/")!, protocols: ["chat","superchat"]) +socket = WebSocket(url: URL(string: "ws://localhost:8080/")!, protocols: ["chat","superchat"]) let data = ... //load your certificate from disk socket.security = SSLSecurity(certs: [SSLCert(data: data)], usePublicKeys: true) //socket.security = SSLSecurity() //uses the .cer files in your app's bundle ``` -You load either a `NSData` blob of your certificate or you can use a `SecKeyRef` if you have a public key you want to use. The `usePublicKeys` bool is whether to use the certificates for validation or the public keys. The public keys will be extracted from the certificates automatically if `usePublicKeys` is choosen. +You load either a `Data` blob of your certificate or you can use a `SecKeyRef` if you have a public key you want to use. The `usePublicKeys` bool is whether to use the certificates for validation or the public keys. The public keys will be extracted from the certificates automatically if `usePublicKeys` is choosen. ### Custom Queue -A custom queue can be specified when delegate methods are called. By default `dispatch_get_main_queue` is used, thus making all delegate methods calls run on the main thread. It is important to note that all WebSocket processing is done on a background thread, only the delegate method calls are changed when modifying the queue. The actual processing is always on a background thread and will not pause your app. +A custom queue can be specified when delegate methods are called. By default `DispatchQueue.main` is used, thus making all delegate methods calls run on the main thread. It is important to note that all WebSocket processing is done on a background thread, only the delegate method calls are changed when modifying the queue. The actual processing is always on a background thread and will not pause your app. ```swift -socket = WebSocket(url: NSURL(string: "ws://localhost:8080/")!, protocols: ["chat","superchat"]) +socket = WebSocket(url: URL(string: "ws://localhost:8080/")!, protocols: ["chat","superchat"]) //create a custom queue -socket.queue = dispatch_queue_create("com.vluxe.starscream.myapp", nil) +socket.callbackQueue = DispatchQueue(label: "com.vluxe.starscream.myapp") ``` ## Example Project @@ -230,7 +227,7 @@ To use Starscream in your project add the following 'Podfile' to your project platform :ios, '9.0' use_frameworks! - pod 'Starscream', '~> 1.1.3' + pod 'Starscream', '~> 2.0.0' Then run: @@ -252,7 +249,7 @@ $ brew install carthage To integrate Starscream into your Xcode project using Carthage, specify it in your `Cartfile`: ``` -github "daltoniam/Starscream" >= 1.1.3 +github "daltoniam/Starscream" >= 2.0.0 ``` ### Rogue diff --git a/Source/Info-tvOS.plist b/Source/Info-tvOS.plist index deb1d1e..5bba407 100644 --- a/Source/Info-tvOS.plist +++ b/Source/Info-tvOS.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.1.3 + 2.0.0 CFBundleSignature ???? CFBundleVersion diff --git a/Source/Info.plist b/Source/Info.plist index 0c61092..7e7479f 100644 --- a/Source/Info.plist +++ b/Source/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.1.3 + 2.0.0 CFBundleSignature ???? CFBundleVersion diff --git a/Source/SSLSecurity.swift b/Source/SSLSecurity.swift index 90e0a09..d9134c3 100644 --- a/Source/SSLSecurity.swift +++ b/Source/SSLSecurity.swift @@ -4,7 +4,7 @@ // Starscream // // Created by Dalton Cherry on 5/16/15. -// Copyright (c) 2014-2015 Dalton Cherry. +// Copyright (c) 2014-2016 Dalton Cherry. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/Source/WebSocket.swift b/Source/WebSocket.swift index 0b87e4f..1aa5e5b 100644 --- a/Source/WebSocket.swift +++ b/Source/WebSocket.swift @@ -3,7 +3,7 @@ // Websocket.swift // // Created by Dalton Cherry on 7/16/14. -// Copyright (c) 2014-2015 Dalton Cherry. +// Copyright (c) 2014-2016 Dalton Cherry. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -28,14 +28,14 @@ public let WebsocketDidDisconnectNotification = "WebsocketDidDisconnectNotificat public let WebsocketDisconnectionErrorKeyName = "WebsocketDisconnectionErrorKeyName" public protocol WebSocketDelegate: class { - func websocketDidConnect(_ socket: WebSocket) - func websocketDidDisconnect(_ socket: WebSocket, error: NSError?) - func websocketDidReceiveMessage(_ socket: WebSocket, text: String) - func websocketDidReceiveData(_ socket: WebSocket, data: Data) + func websocketDidConnect(socket: WebSocket) + func websocketDidDisconnect(socket: WebSocket, error: NSError?) + func websocketDidReceiveMessage(socket: WebSocket, text: String) + func websocketDidReceiveData(socket: WebSocket, data: Data) } public protocol WebSocketPongDelegate: class { - func websocketDidReceivePong(_ socket: WebSocket) + func websocketDidReceivePong(socket: WebSocket, data: Data?) } public class WebSocket : NSObject, StreamDelegate { @@ -72,9 +72,9 @@ public class WebSocket : NSObject, StreamDelegate { } // Where the callback is executed. It defaults to the main UI thread queue. - public var callbackQueue = DispatchQueue.main + public var callbackQueue = DispatchQueue.main - var optionalProtocols : [String]? + var optionalProtocols: [String]? // MARK: - Constants @@ -113,7 +113,7 @@ public class WebSocket : NSObject, StreamDelegate { /// and also connection/disconnect messages. public weak var delegate: WebSocketDelegate? - /// Recives a callback for each pong message recived. + /// Receives a callback for each pong message recived. public weak var pongDelegate: WebSocketPongDelegate? @@ -123,16 +123,16 @@ public class WebSocket : NSObject, StreamDelegate { public var onDisconnect: ((NSError?) -> Void)? public var onText: ((String) -> Void)? public var onData: ((Data) -> Void)? - public var onPong: ((Void) -> Void)? + public var onPong: ((Data?) -> Void)? public var headers = [String: String]() public var voipEnabled = false - public var selfSignedSSL = false + public var disableSSLCertValidation = false public var security: SSLSecurity? public var enabledSSLCipherSuites: [SSLCipherSuite]? public var origin: String? public var timeout = 5 - public var isConnected :Bool { + public var isConnected: Bool { return connected } @@ -171,7 +171,9 @@ public class WebSocket : NSObject, StreamDelegate { optionalProtocols = protocols } - /// Connect to the WebSocket server on a background thread. + /** + Connect to the WebSocket server on a background thread. + */ public func connect() { guard !isConnecting else { return } didDisconnect = false @@ -188,8 +190,9 @@ public class WebSocket : NSObject, StreamDelegate { If you supply a zero (or negative) `forceTimeout`, I immediately close the socket (without sending a Close control frame) and notify my delegate. - Parameter forceTimeout: Maximum time to wait for the server to close the socket. + - Parameter closeCode: The code to send on disconnect. The default is the normal close code for cleanly disconnecting a webSocket. */ - public func disconnect(forceTimeout: TimeInterval? = nil) { + public func disconnect(forceTimeout: TimeInterval? = nil, closeCode: UInt16 = CloseCode.normal.rawValue) { switch forceTimeout { case .some(let seconds) where seconds > 0: callbackQueue.asyncAfter(deadline: DispatchTime.now() + Double(Int64(seconds * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)) { [weak self] in @@ -197,7 +200,7 @@ public class WebSocket : NSObject, StreamDelegate { } fallthrough case .none: - writeError(CloseCode.normal.rawValue) + writeError(closeCode) default: disconnectStream(nil) break @@ -230,14 +233,18 @@ public class WebSocket : NSObject, StreamDelegate { 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 write(_ ping: Data, completion: (() -> ())? = nil) { + /** + 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 write(ping: Data, completion: (() -> ())? = nil) { guard isConnected else { return } dequeueWrite(ping, code: .ping, writeCompletion: completion) } - /// Private method that starts the connection. + /** + Private method that starts the connection. + */ private func createHTTPRequest() { let urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, "GET" as CFString, @@ -262,7 +269,7 @@ public class WebSocket : NSObject, StreamDelegate { addHeader(urlRequest, key: headerOriginName, val: origin) } addHeader(urlRequest, key: headerWSHostName, val: "\(url.host!):\(port!)") - for (key,value) in headers { + for (key, value) in headers { addHeader(urlRequest, key: key, val: value) } if let cfHTTPMessage = CFHTTPMessageCopySerializedMessage(urlRequest) { @@ -271,12 +278,16 @@ public class WebSocket : NSObject, StreamDelegate { } } - // Add a header to the CFHTTPMessage by using the NSString bridges to CFString + /** + Add a header to the CFHTTPMessage by using the NSString bridges to CFString + */ private func addHeader(_ urlRequest: CFHTTPMessage, key: String, val: String) { CFHTTPMessageSetHeaderFieldValue(urlRequest, key as CFString, val as CFString) } - /// Generate a WebSocket key as needed in RFC. + /** + Generate a WebSocket key as needed in RFC. + */ private func generateWebSocketKey() -> String { var key = "" let seed = 16 @@ -289,7 +300,9 @@ public class WebSocket : NSObject, StreamDelegate { return baseKey! } - /// Start the stream connection and write the data to the output stream. + /** + Start the stream connection and write the data to the output stream. + */ private func initStreamsWithData(_ data: Data, _ port: Int) { //higher level API we will cut over to at some point //NSStream.getStreamsToHostWithName(url.host, port: url.port.integerValue, inputStream: &inputStream, outputStream: &outputStream) @@ -313,7 +326,7 @@ public class WebSocket : NSObject, StreamDelegate { inStream.setProperty(StreamNetworkServiceTypeValue.voIP as AnyObject, forKey: Stream.PropertyKey.networkServiceType) outStream.setProperty(StreamNetworkServiceTypeValue.voIP as AnyObject, forKey: Stream.PropertyKey.networkServiceType) } - if selfSignedSSL { + if disableSSLCertValidation { let settings: [NSObject: NSObject] = [kCFStreamSSLValidatesCertificateChain: NSNumber(value: false), kCFStreamSSLPeerName: kCFNull] inStream.setProperty(settings, forKey: kCFStreamPropertySSLSettings as Stream.PropertyKey) outStream.setProperty(settings, forKey: kCFStreamPropertySSLSettings as Stream.PropertyKey) @@ -363,7 +376,9 @@ public class WebSocket : NSObject, StreamDelegate { } } - //delegate for the stream methods. Processes incoming bytes + /** + Delegate for the stream methods. Processes incoming bytes + */ public func stream(_ aStream: Stream, handle eventCode: Stream.Event) { if let sec = security, !certValidated && [.hasBytesAvailable, .hasSpaceAvailable].contains(eventCode) { let trust = aStream.property(forKey: kCFStreamPropertySSLPeerTrust as Stream.PropertyKey) as AnyObject @@ -387,7 +402,9 @@ public class WebSocket : NSObject, StreamDelegate { } } - /// Disconnect the stream object and notifies the delegate. + /** + Disconnect the stream object and notifies the delegate. + */ private func disconnectStream(_ error: NSError?) { if error == nil { writeQueue.waitUntilAllOperationsAreFinished() @@ -398,6 +415,9 @@ public class WebSocket : NSObject, StreamDelegate { doDisconnect(error) } + /** + cleanup the streams. + */ private func cleanupStream() { outputStream?.delegate = nil inputStream?.delegate = nil @@ -413,7 +433,9 @@ public class WebSocket : NSObject, StreamDelegate { inputStream = nil } - /// Handles the incoming bytes and sending them to the proper processing method. + /** + Handles the incoming bytes and sending them to the proper processing method. + */ private func processInputStream() { let buf = NSMutableData(capacity: BUFFER_MAX) let buffer = UnsafeMutableRawPointer(mutating: buf!.bytes).assumingMemoryBound(to: UInt8.self) @@ -430,7 +452,9 @@ public class WebSocket : NSObject, StreamDelegate { } } - /// Dequeue the incoming input so it is processed in order. + /** + Dequeue the incoming input so it is processed in order. + */ private func dequeueInput() { while !inputQueue.isEmpty { let data = inputQueue[0] @@ -452,7 +476,9 @@ public class WebSocket : NSObject, StreamDelegate { } } - //handle checking the inital connection status + /** + Handle checking the inital connection status + */ private func processTCPHandshake(_ buffer: UnsafePointer, bufferLen: Int) { let code = processHTTP(buffer, bufferLen: bufferLen) switch code { @@ -462,7 +488,7 @@ public class WebSocket : NSObject, StreamDelegate { callbackQueue.async { [weak self] in guard let s = self else { return } s.onConnect?() - s.delegate?.websocketDidConnect(s) + s.delegate?.websocketDidConnect(socket: s) s.notificationCenter.post(name: NSNotification.Name(WebsocketDidConnectNotification), object: self) } case -1: @@ -473,7 +499,9 @@ public class WebSocket : NSObject, StreamDelegate { } } - ///Finds the HTTP Packet in the TCP stream, by looking for the CRLF. + /** + Finds the HTTP Packet in the TCP stream, by looking for the CRLF. + */ private func processHTTP(_ buffer: UnsafePointer, bufferLen: Int) -> Int { let CRLFBytes = [UInt8(ascii: "\r"), UInt8(ascii: "\n"), UInt8(ascii: "\r"), UInt8(ascii: "\n")] var k = 0 @@ -504,7 +532,9 @@ public class WebSocket : NSObject, StreamDelegate { return -1 // Was unable to find the full TCP header. } - /// Validates the HTTP is a 101 as per the RFC spec. + /** + Validates the HTTP is a 101 as per the RFC spec. + */ private func validateResponse(_ buffer: UnsafePointer, bufferLen: Int) -> Int { let response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, false).takeRetainedValue() CFHTTPMessageAppendBytes(response, buffer, bufferLen) @@ -523,12 +553,16 @@ public class WebSocket : NSObject, StreamDelegate { return -1 } - ///read a 16 bit big endian value from a buffer + /** + Read a 16 bit big endian value from a buffer + */ private static func readUint16(_ buffer: UnsafePointer, offset: Int) -> UInt16 { return (UInt16(buffer[offset + 0]) << 8) | UInt16(buffer[offset + 1]) } - ///read a 64 bit big endian value from a buffer + /** + Read a 64 bit big endian value from a buffer + */ private static func readUint64(_ buffer: UnsafePointer, offset: Int) -> UInt64 { var value = UInt64(0) for i in 0...7 { @@ -537,26 +571,31 @@ public class WebSocket : NSObject, StreamDelegate { return value } - /// Write a 16-bit big endian value to a buffer. + /** + Write a 16-bit big endian value to a buffer. + */ private static func writeUint16(_ buffer: UnsafeMutablePointer, offset: Int, value: UInt16) { buffer[offset + 0] = UInt8(value >> 8) buffer[offset + 1] = UInt8(value & 0xff) } - /// Write a 64-bit big endian value to a buffer. + /** + Write a 64-bit big endian value to a buffer. + */ private static func writeUint64(_ buffer: UnsafeMutablePointer, offset: Int, value: UInt64) { for i in 0...7 { buffer[offset + i] = UInt8((value >> (8*UInt64(7 - i))) & 0xff) } } - /// Process one message at the start of `buffer`. Return another buffer (sharing storage) that contains the leftover contents of `buffer` that I didn't process. - + /** + Process one message at the start of `buffer`. Return another buffer (sharing storage) that contains the leftover contents of `buffer` that I didn't process. + */ private func processOneRawMessage(inBuffer buffer: UnsafeBufferPointer) -> UnsafeBufferPointer { let response = readStack.last guard let baseAddress = buffer.baseAddress else {return emptyBuffer} let bufferLen = buffer.count - if response != nil && bufferLen < 2 { + if response != nil && bufferLen < 2 { fragBuffer = Data(buffer: buffer) return emptyBuffer } @@ -655,8 +694,9 @@ public class WebSocket : NSObject, StreamDelegate { if canDispatch { callbackQueue.async { [weak self] in guard let s = self else { return } - s.onPong?() - s.pongDelegate?.websocketDidReceivePong(s) + let pongData: Data? = data.count > 0 ? data : nil + s.onPong?(pongData) + s.pongDelegate?.websocketDidReceivePong(socket: s, data: pongData) } } return buffer.fromOffset(offset + Int(len)) @@ -673,7 +713,7 @@ public class WebSocket : NSObject, StreamDelegate { } var isNew = false if response == nil { - if receivedOpcode == .continueFrame { + if receivedOpcode == .continueFrame { let errCode = CloseCode.protocolError.rawValue doDisconnect(errorWithDetail("first frame can't be a continue frame", code: errCode)) @@ -686,7 +726,7 @@ public class WebSocket : NSObject, StreamDelegate { response!.bytesLeft = Int(dataLength) response!.buffer = NSMutableData(data: data) } else { - if receivedOpcode == .continueFrame { + if receivedOpcode == .continueFrame { response!.bytesLeft = Int(dataLength) } else { let errCode = CloseCode.protocolError.rawValue @@ -712,7 +752,9 @@ public class WebSocket : NSObject, StreamDelegate { } } - /// Process all messages in the buffer if possible. + /** + Process all messages in the buffer if possible. + */ private func processRawMessagesInBuffer(_ pointer: UnsafePointer, bufferLen: Int) { var buffer = UnsafeBufferPointer(start: pointer, count: bufferLen) repeat { @@ -723,7 +765,9 @@ public class WebSocket : NSObject, StreamDelegate { } } - /// Process the finished response of a buffer. + /** + Process the finished response of a buffer. + */ private func processResponse(_ response: WSResponse) -> Bool { if response.isFin && response.bytesLeft <= 0 { if response.code == .ping { @@ -739,7 +783,7 @@ public class WebSocket : NSObject, StreamDelegate { callbackQueue.async { [weak self] in guard let s = self else { return } s.onText?(str! as String) - s.delegate?.websocketDidReceiveMessage(s, text: str! as String) + s.delegate?.websocketDidReceiveMessage(socket: s, text: str! as String) } } } else if response.code == .binaryFrame { @@ -748,7 +792,7 @@ public class WebSocket : NSObject, StreamDelegate { callbackQueue.async { [weak self] in guard let s = self else { return } s.onData?(data as Data) - s.delegate?.websocketDidReceiveData(s, data: data as Data) + s.delegate?.websocketDidReceiveData(socket: s, data: data as Data) } } } @@ -758,14 +802,18 @@ public class WebSocket : NSObject, StreamDelegate { return false } - /// Create an error + /** + Create an error + */ private func errorWithDetail(_ detail: String, code: UInt16) -> NSError { var details = [String: String]() details[NSLocalizedDescriptionKey] = detail return NSError(domain: WebSocket.ErrorDomain, code: Int(code), userInfo: details) } - /// Write an error to the socket + /** + Write an error to the socket + */ private func writeError(_ code: UInt16) { let buf = NSMutableData(capacity: MemoryLayout.size) let buffer = UnsafeMutableRawPointer(mutating: buf!.bytes).assumingMemoryBound(to: UInt8.self) @@ -773,7 +821,9 @@ public class WebSocket : NSObject, StreamDelegate { dequeueWrite(Data(bytes: buffer, count: MemoryLayout.size), code: .connectionClose) } - /// Used to write things to the stream + /** + Used to write things to the stream + */ private func dequeueWrite(_ data: Data, code: OpCode, writeCompletion: (() -> ())? = nil) { writeQueue.addOperation { [weak self] in //stream isn't ready, let's wait @@ -835,7 +885,9 @@ public class WebSocket : NSObject, StreamDelegate { } } - /// Used to preform the disconnect delegate + /** + Used to preform the disconnect delegate + */ private func doDisconnect(_ error: NSError?) { guard !didDisconnect else { return } didDisconnect = true @@ -844,7 +896,7 @@ public class WebSocket : NSObject, StreamDelegate { callbackQueue.async { [weak self] in guard let s = self else { return } s.onDisconnect?(error) - s.delegate?.websocketDidDisconnect(s, error: error) + s.delegate?.websocketDidDisconnect(socket: s, error: error) let userInfo = error.map{ [WebsocketDisconnectionErrorKeyName: $0] } s.notificationCenter.post(name: NSNotification.Name(WebsocketDidDisconnectNotification), object: self, userInfo: userInfo) } diff --git a/Starscream.podspec b/Starscream.podspec index ab2b760..fe9541c 100644 --- a/Starscream.podspec +++ b/Starscream.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Starscream" - s.version = "1.1.3" + s.version = "2.0.0" s.summary = "A conforming WebSocket RFC 6455 client library in Swift for iOS and OSX." s.homepage = "https://github.com/daltoniam/Starscream" s.license = 'Apache License, Version 2.0' diff --git a/examples/AutobahnTest/Autobahn.xcodeproj/project.pbxproj b/examples/AutobahnTest/Autobahn.xcodeproj/project.pbxproj index 517255b..2a31d53 100644 --- a/examples/AutobahnTest/Autobahn.xcodeproj/project.pbxproj +++ b/examples/AutobahnTest/Autobahn.xcodeproj/project.pbxproj @@ -59,6 +59,20 @@ remoteGlobalIDString = 6B3E79E519D48B7F006071F7; remoteInfo = Starscream; }; + 5C42C3D51D8DF51C00947AA2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5C178E411B62D0EF00A97204 /* Starscream.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 091277971BD673A70003036D; + remoteInfo = "Starscream tvOS"; + }; + 5C42C3D71D8DF51C00947AA2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5C178E411B62D0EF00A97204 /* Starscream.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 091277A01BD673A70003036D; + remoteInfo = "Starscream tvOSTests"; + }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ @@ -158,6 +172,8 @@ 5C178E4B1B62D0EF00A97204 /* StarscreamTests.xctest */, 5C178E4D1B62D0EF00A97204 /* Starscream.framework */, 5C178E4F1B62D0EF00A97204 /* StarscreamOSXTests.xctest */, + 5C42C3D61D8DF51C00947AA2 /* Starscream.framework */, + 5C42C3D81D8DF51C00947AA2 /* Starscream tvOSTests.xctest */, ); name = Products; sourceTree = ""; @@ -214,9 +230,11 @@ TargetAttributes = { 5C178E1B1B62D0B900A97204 = { CreatedOnToolsVersion = 6.4; + LastSwiftMigration = 0800; }; 5C178E301B62D0B900A97204 = { CreatedOnToolsVersion = 6.4; + LastSwiftMigration = 0800; TestTargetID = 5C178E1B1B62D0B900A97204; }; }; @@ -257,7 +275,7 @@ 5C178E4B1B62D0EF00A97204 /* StarscreamTests.xctest */ = { isa = PBXReferenceProxy; fileType = wrapper.cfbundle; - path = StarscreamTests.xctest; + path = "Starscream iOSTests.xctest"; remoteRef = 5C178E4A1B62D0EF00A97204 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -271,10 +289,24 @@ 5C178E4F1B62D0EF00A97204 /* StarscreamOSXTests.xctest */ = { isa = PBXReferenceProxy; fileType = wrapper.cfbundle; - path = StarscreamOSXTests.xctest; + path = "Starscream OSXTests.xctest"; remoteRef = 5C178E4E1B62D0EF00A97204 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 5C42C3D61D8DF51C00947AA2 /* Starscream.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Starscream.framework; + remoteRef = 5C42C3D51D8DF51C00947AA2 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 5C42C3D81D8DF51C00947AA2 /* Starscream tvOSTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "Starscream tvOSTests.xctest"; + remoteRef = 5C42C3D71D8DF51C00947AA2 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; /* End PBXReferenceProxy section */ /* Begin PBXResourcesBuildPhase section */ @@ -440,6 +472,7 @@ INFOPLIST_FILE = Autobahn/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -450,6 +483,7 @@ INFOPLIST_FILE = Autobahn/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; }; name = Release; }; @@ -468,6 +502,7 @@ INFOPLIST_FILE = AutobahnTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Autobahn.app/Autobahn"; }; name = Debug; @@ -483,6 +518,7 @@ INFOPLIST_FILE = AutobahnTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Autobahn.app/Autobahn"; }; name = Release; diff --git a/examples/AutobahnTest/Autobahn/AppDelegate.swift b/examples/AutobahnTest/Autobahn/AppDelegate.swift index f366c3e..7e1a59a 100644 --- a/examples/AutobahnTest/Autobahn/AppDelegate.swift +++ b/examples/AutobahnTest/Autobahn/AppDelegate.swift @@ -14,30 +14,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } - func applicationWillResignActive(application: UIApplication) { + func applicationWillResignActive(_ application: UIApplication) { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. } - func applicationDidEnterBackground(application: UIApplication) { + func applicationDidEnterBackground(_ application: UIApplication) { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. } - func applicationWillEnterForeground(application: UIApplication) { + func applicationWillEnterForeground(_ application: UIApplication) { // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. } - func applicationDidBecomeActive(application: UIApplication) { + func applicationDidBecomeActive(_ application: UIApplication) { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. } - func applicationWillTerminate(application: UIApplication) { + func applicationWillTerminate(_ application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } diff --git a/examples/AutobahnTest/Autobahn/ViewController.swift b/examples/AutobahnTest/Autobahn/ViewController.swift index a6ff607..139c022 100644 --- a/examples/AutobahnTest/Autobahn/ViewController.swift +++ b/examples/AutobahnTest/Autobahn/ViewController.swift @@ -12,7 +12,6 @@ import Starscream class ViewController: UIViewController { let host = "localhost:9001" - let scheme = "ws" var socketArray = [WebSocket]() var caseCount = 300 //starting cases override func viewDidLoad() { @@ -21,12 +20,13 @@ class ViewController: UIViewController { //getTestInfo(1) } - func removeSocket(s: WebSocket) { - self.socketArray = self.socketArray.filter{$0 != s} + func removeSocket(_ s: WebSocket) { + socketArray = socketArray.filter{$0 != s} } func getCaseCount() { - let s = WebSocket(url: NSURL(scheme: scheme, host: host, path: "/getCaseCount")!, protocols: []) + + let s = WebSocket(url: URL(string: "ws://\(host)/getCaseCount")!, protocols: []) socketArray.append(s) s.onText = {[unowned self] (text: String) in if let c = Int(text) { @@ -41,7 +41,7 @@ class ViewController: UIViewController { s.connect() } - func getTestInfo(caseNum: Int) { + func getTestInfo(_ caseNum: Int) { let s = createSocket("getCaseInfo",caseNum) socketArray.append(s) s.onText = {(text: String) in @@ -72,14 +72,14 @@ class ViewController: UIViewController { s.connect() } - func runTest(caseNum: Int) { + func runTest(_ caseNum: Int) { let s = createSocket("runCase",caseNum) self.socketArray.append(s) s.onText = {(text: String) in - s.writeString(text) + s.write(string: text) } - s.onData = {(data: NSData) in - s.writeData(data) + s.onData = {(data: Data) in + s.write(data: data) } var once = false s.onDisconnect = {[unowned self] (error: NSError?) in @@ -93,14 +93,14 @@ class ViewController: UIViewController { s.connect() } - func verifyTest(caseNum: Int) { + func verifyTest(_ caseNum: Int) { let s = createSocket("getCaseStatus",caseNum) self.socketArray.append(s) s.onText = {(text: String) in - let data = text.dataUsingEncoding(NSUTF8StringEncoding) + let data = text.data(using: String.Encoding.utf8) do { - let resp: AnyObject? = try NSJSONSerialization.JSONObjectWithData(data!, - options: NSJSONReadingOptions()) + let resp: Any? = try JSONSerialization.jsonObject(with: data!, + options: JSONSerialization.ReadingOptions()) if let dict = resp as? Dictionary { if let status = dict["behavior"] { if status == "OK" { @@ -140,12 +140,11 @@ class ViewController: UIViewController { s.connect() } - func createSocket(cmd: String, _ caseNum: Int) -> WebSocket { - return WebSocket(url: NSURL(scheme: scheme, - host: host, path: buildPath(cmd,caseNum))!, protocols: []) + func createSocket(_ cmd: String, _ caseNum: Int) -> WebSocket { + return WebSocket(url: URL(string: "ws://\(host)\(buildPath(cmd,caseNum))")!, protocols: []) } - func buildPath(cmd: String, _ caseNum: Int) -> String { + func buildPath(_ cmd: String, _ caseNum: Int) -> String { return "/\(cmd)?case=\(caseNum)&agent=Starscream" } diff --git a/examples/AutobahnTest/AutobahnTests/AutobahnTests.swift b/examples/AutobahnTest/AutobahnTests/AutobahnTests.swift index 746eee0..6eccc03 100644 --- a/examples/AutobahnTest/AutobahnTests/AutobahnTests.swift +++ b/examples/AutobahnTest/AutobahnTests/AutobahnTests.swift @@ -28,7 +28,7 @@ class AutobahnTests: XCTestCase { func testPerformanceExample() { // This is an example of a performance test case. - self.measureBlock() { + self.measure() { // Put the code you want to measure the time of here. } } diff --git a/examples/SimpleTest/SimpleTest.xcodeproj/project.pbxproj b/examples/SimpleTest/SimpleTest.xcodeproj/project.pbxproj index f665352..8c8529a 100644 --- a/examples/SimpleTest/SimpleTest.xcodeproj/project.pbxproj +++ b/examples/SimpleTest/SimpleTest.xcodeproj/project.pbxproj @@ -37,6 +37,20 @@ remoteGlobalIDString = D9C3E36919E48FF1009FC285; remoteInfo = StarscreamOSXTests; }; + 5C42C3E01D8F31DC00947AA2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6B3E7A0819D48D00006071F7 /* Starscream.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 091277971BD673A70003036D; + remoteInfo = "Starscream tvOS"; + }; + 5C42C3E21D8F31DC00947AA2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6B3E7A0819D48D00006071F7 /* Starscream.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 091277A01BD673A70003036D; + remoteInfo = "Starscream tvOSTests"; + }; 6B3E7A0D19D48D00006071F7 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 6B3E7A0819D48D00006071F7 /* Starscream.xcodeproj */; @@ -132,6 +146,8 @@ 6B3E7A1019D48D00006071F7 /* StarscreamTests.xctest */, 5C06AE8B1B08044600D41060 /* Starscream.framework */, 5C06AE8D1B08044600D41060 /* StarscreamOSXTests.xctest */, + 5C42C3E11D8F31DC00947AA2 /* Starscream.framework */, + 5C42C3E31D8F31DC00947AA2 /* Starscream tvOSTests.xctest */, ); name = Products; sourceTree = ""; @@ -170,6 +186,7 @@ TargetAttributes = { 5C765ADA199A6DAA003D9110 = { CreatedOnToolsVersion = 6.0; + LastSwiftMigration = 0800; }; }; }; @@ -208,10 +225,24 @@ 5C06AE8D1B08044600D41060 /* StarscreamOSXTests.xctest */ = { isa = PBXReferenceProxy; fileType = wrapper.cfbundle; - path = StarscreamOSXTests.xctest; + path = "Starscream OSXTests.xctest"; remoteRef = 5C06AE8C1B08044600D41060 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 5C42C3E11D8F31DC00947AA2 /* Starscream.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Starscream.framework; + remoteRef = 5C42C3E01D8F31DC00947AA2 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 5C42C3E31D8F31DC00947AA2 /* Starscream tvOSTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "Starscream tvOSTests.xctest"; + remoteRef = 5C42C3E21D8F31DC00947AA2 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 6B3E7A0E19D48D00006071F7 /* Starscream.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; @@ -222,7 +253,7 @@ 6B3E7A1019D48D00006071F7 /* StarscreamTests.xctest */ = { isa = PBXReferenceProxy; fileType = wrapper.cfbundle; - path = StarscreamTests.xctest; + path = "Starscream iOSTests.xctest"; remoteRef = 6B3E7A0F19D48D00006071F7 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -359,6 +390,7 @@ INFOPLIST_FILE = SimpleTest/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -370,6 +402,7 @@ INFOPLIST_FILE = SimpleTest/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; }; name = Release; }; diff --git a/examples/SimpleTest/SimpleTest.xcodeproj/project.xcworkspace/xcuserdata/dalton.xcuserdatad/UserInterfaceState.xcuserstate b/examples/SimpleTest/SimpleTest.xcodeproj/project.xcworkspace/xcuserdata/dalton.xcuserdatad/UserInterfaceState.xcuserstate index 20a1622..a729a2d 100644 Binary files a/examples/SimpleTest/SimpleTest.xcodeproj/project.xcworkspace/xcuserdata/dalton.xcuserdatad/UserInterfaceState.xcuserstate and b/examples/SimpleTest/SimpleTest.xcodeproj/project.xcworkspace/xcuserdata/dalton.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/examples/SimpleTest/SimpleTest/AppDelegate.swift b/examples/SimpleTest/SimpleTest/AppDelegate.swift index 51a3a46..61a4ee5 100644 --- a/examples/SimpleTest/SimpleTest/AppDelegate.swift +++ b/examples/SimpleTest/SimpleTest/AppDelegate.swift @@ -14,30 +14,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } - func applicationWillResignActive(application: UIApplication) { + func applicationWillResignActive(_ application: UIApplication) { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. } - func applicationDidEnterBackground(application: UIApplication) { + func applicationDidEnterBackground(_ application: UIApplication) { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. } - func applicationWillEnterForeground(application: UIApplication) { + func applicationWillEnterForeground(_ application: UIApplication) { // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. } - func applicationDidBecomeActive(application: UIApplication) { + func applicationDidBecomeActive(_ application: UIApplication) { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. } - func applicationWillTerminate(application: UIApplication) { + func applicationWillTerminate(_ application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } diff --git a/examples/SimpleTest/SimpleTest/ViewController.swift b/examples/SimpleTest/SimpleTest/ViewController.swift index 2c55d29..379f567 100644 --- a/examples/SimpleTest/SimpleTest/ViewController.swift +++ b/examples/SimpleTest/SimpleTest/ViewController.swift @@ -10,7 +10,7 @@ import UIKit import Starscream class ViewController: UIViewController, WebSocketDelegate { - var socket = WebSocket(url: NSURL(string: "ws://localhost:8080/")!, protocols: ["chat", "superchat"]) + var socket = WebSocket(url: URL(string: "ws://localhost:8080/")!, protocols: ["chat", "superchat"]) override func viewDidLoad() { super.viewDidLoad() @@ -20,11 +20,11 @@ class ViewController: UIViewController, WebSocketDelegate { // MARK: Websocket Delegate Methods. - func websocketDidConnect(ws: WebSocket) { + func websocketDidConnect(socket: WebSocket) { print("websocket is connected") } - func websocketDidDisconnect(ws: WebSocket, error: NSError?) { + func websocketDidDisconnect(socket: WebSocket, error: NSError?) { if let e = error { print("websocket is disconnected: \(e.localizedDescription)") } else { @@ -32,23 +32,23 @@ class ViewController: UIViewController, WebSocketDelegate { } } - func websocketDidReceiveMessage(ws: WebSocket, text: String) { + func websocketDidReceiveMessage(socket: WebSocket, text: String) { print("Received text: \(text)") } - func websocketDidReceiveData(ws: WebSocket, data: NSData) { - print("Received data: \(data.length)") + func websocketDidReceiveData(socket: WebSocket, data: Data) { + print("Received data: \(data.count)") } // MARK: Write Text Action - @IBAction func writeText(sender: UIBarButtonItem) { - socket.writeString("hello there!") + @IBAction func writeText(_ sender: UIBarButtonItem) { + socket.write(string: "hello there!") } // MARK: Disconnect Action - @IBAction func disconnect(sender: UIBarButtonItem) { + @IBAction func disconnect(_ sender: UIBarButtonItem) { if socket.isConnected { sender.title = "Connect" socket.disconnect()