Skip to content

Commit

Permalink
fixes for #98 and #99. Closures are way more awesome. Possible fix for
Browse files Browse the repository at this point in the history
…#87, #77 and fix for #72
  • Loading branch information
daltoniam committed Jul 10, 2015
1 parent b3db9d4 commit 7b19d84
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 52 deletions.
37 changes: 31 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import Starscream
Once imported, you can open a connection to your WebSocket server. Note that `socket` is probably best as a property, so your delegate can stick around.

```swift
var socket = WebSocket(url: NSURL(scheme: "ws", host: "localhost:8080", path: "/"))
var socket = WebSocket(url: NSURL(scheme: "ws", host: "localhost:8080", path: "/")!)
socket.delegate = self
socket.connect()
```
Expand Down Expand Up @@ -81,6 +81,31 @@ func websocketDidReceivePong(socket: WebSocket) {
}
```

Or you can use closures.

```swift
var socket = WebSocket(url: NSURL(scheme: "ws", host: "localhost:8080", path: "/")!)
//websocketDidConnect
socket.onConnect = {
println("websocket is connected")
}
//websocketDidDisconnect
socket.onDisconnect = { (error: NSError?) in
println("websocket is disconnected: \(error?.localizedDescription)")
}
//websocketDidReceiveMessage
socket.onText = { (text: String) in
println("got some text: \(text)")
}
//websocketDidReceiveData
socket.onData = { (data: NSData) in
println("got some data: \(data.length)")
}
//you could do onPong as well.
socket.connect()
```


## The delegate methods give you a simple way to handle data from the server, but how do you send data?

### writeData
Expand Down Expand Up @@ -141,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
var socket = WebSocket(url: NSURL(scheme: "ws", host: "localhost:8080", path: "/"), protocols: ["chat","superchat"])
var socket = WebSocket(url: NSURL(scheme: "ws", host: "localhost:8080", path: "/")!, protocols: ["chat","superchat"])
socket.delegate = self
socket.connect()
```
Expand All @@ -151,7 +176,7 @@ socket.connect()
There are a couple of other properties that modify the stream:

```swift
var socket = WebSocket(url: NSURL(scheme: "ws", host: "localhost:8080", path: "/"), protocols: ["chat","superchat"])
var socket = WebSocket(url: NSURL(scheme: "ws", host: "localhost:8080", path: "/")!, 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
Expand All @@ -165,7 +190,7 @@ socket.selfSignedSSL = true
SSL Pinning is also supported in Starscream.

```swift
var socket = WebSocket(url: NSURL(scheme: "ws", host: "localhost:8080", path: "/"), protocols: ["chat","superchat"])
var socket = WebSocket(url: NSURL(scheme: "ws", host: "localhost:8080", path: "/")!, protocols: ["chat","superchat"])
let data = ... //load your certificate from disk
socket.security = Security(certs: [SSLCert(data: data)], usePublicKeys: true)
//socket.security = Security() //uses the .cer files in your app's bundle
Expand All @@ -177,7 +202,7 @@ You load either a `NSData` blob of your certificate or you can use a `SecKeyRef`
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.

```swift
var socket = WebSocket(url: NSURL(scheme: "ws", host: "localhost:8080", path: "/"), protocols: ["chat","superchat"])
var socket = WebSocket(url: NSURL(scheme: "ws", host: "localhost:8080", path: "/")!, protocols: ["chat","superchat"])
//create a custom queue
socket.queue = dispatch_queue_create("com.vluxe.starscream.myapp", nil)
```
Expand All @@ -202,7 +227,7 @@ To use Starscream in your project add the following 'Podfile' to your project
platform :ios, '8.0'
use_frameworks!

pod 'Starscream', '~> 0.9.2'
pod 'Starscream', '~> 0.9.4'

Then run:

Expand Down
86 changes: 40 additions & 46 deletions WebSocket.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,18 @@ public class WebSocket : NSObject, NSStreamDelegate {

public weak var delegate: WebSocketDelegate?
public weak var pongDelegate: WebSocketPongDelegate?
public var onConnect: ((Void) -> Void)?
public var onDisconnect: ((NSError?) -> Void)?
public var onText: ((String) -> Void)?
public var onData: ((NSData) -> Void)?
public var onPong: ((Void) -> Void)?
public var headers = Dictionary<String,String>()
public var voipEnabled = false
public var selfSignedSSL = false
public var security: Security?
public var isConnected :Bool {
return connected
}
private var url: NSURL
private var inputStream: NSInputStream?
private var outputStream: NSOutputStream?
Expand All @@ -95,18 +107,8 @@ public class WebSocket : NSObject, NSStreamDelegate {
private var readStack = Array<WSResponse>()
private var inputQueue = Array<NSData>()
private var fragBuffer: NSData?
public var headers = Dictionary<String,String>()
public var voipEnabled = false
public var selfSignedSSL = false
public var security: Security?
private var certValidated = false
private var connectedBlock: ((Void) -> Void)? = nil
private var disconnectedBlock: ((NSError?) -> Void)? = nil
private var receivedTextBlock: ((String) -> Void)? = nil
private var receivedDataBlock: ((NSData) -> Void)? = nil
public var isConnected :Bool {
return connected
}
private var didDisconnect = false

//init the websocket with a url
public init(url: NSURL) {
Expand All @@ -117,34 +119,15 @@ public class WebSocket : NSObject, NSStreamDelegate {
self.init(url: url)
optionalProtocols = protocols
}
//closure based instead of the delegate
public convenience init(url: NSURL, protocols: Array<String>, connect:((Void) -> Void), disconnect:((NSError?) -> Void), text:((String) -> Void), data:(NSData) -> Void) {
self.init(url: url, protocols: protocols)
connectedBlock = connect
disconnectedBlock = disconnect
receivedTextBlock = text
receivedDataBlock = data
}
//same as above, just shorter
public convenience init(url: NSURL, connect:((Void) -> Void), disconnect:((NSError?) -> Void), text:((String) -> Void)) {
self.init(url: url)
connectedBlock = connect
disconnectedBlock = disconnect
receivedTextBlock = text
}
//same as above, just shorter
public convenience init(url: NSURL, connect:((Void) -> Void), disconnect:((NSError?) -> Void), data:((NSData) -> Void)) {
self.init(url: url)
connectedBlock = connect
disconnectedBlock = disconnect
receivedDataBlock = data
}


///Connect to the websocket server on a background thread
public func connect() {
if isCreated {
return
}
dispatch_async(queue,{
self.didDisconnect = false
})
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), {
self.isCreated = true
self.createHTTPRequest()
Expand Down Expand Up @@ -179,7 +162,7 @@ public class WebSocket : NSObject, NSStreamDelegate {

let str: NSString = url.absoluteString!
let urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, "GET",
url, kCFHTTPVersion1_1)
url, kCFHTTPVersion1_1).takeRetainedValue()

var port = url.port
if port == nil {
Expand All @@ -202,14 +185,14 @@ public class WebSocket : NSObject, NSStreamDelegate {
self.addHeader(urlRequest, key: key, val: value)
}

let serializedRequest: NSData = CFHTTPMessageCopySerializedMessage(urlRequest.takeUnretainedValue()).takeUnretainedValue()
let serializedRequest: NSData = CFHTTPMessageCopySerializedMessage(urlRequest).takeRetainedValue()
self.initStreamsWithData(serializedRequest, Int(port!))
}
//Add a header to the CFHTTPMessage by using the NSString bridges to CFString
private func addHeader(urlRequest: Unmanaged<CFHTTPMessage>,key: String, val: String) {
private func addHeader(urlRequest: CFHTTPMessage,key: String, val: String) {
let nsKey: NSString = key
let nsVal: NSString = val
CFHTTPMessageSetHeaderFieldValue(urlRequest.takeUnretainedValue(),
CFHTTPMessageSetHeaderFieldValue(urlRequest,
nsKey,
nsVal)
}
Expand All @@ -234,8 +217,8 @@ public class WebSocket : NSObject, NSStreamDelegate {
var writeStream: Unmanaged<CFWriteStream>?
let h: NSString = url.host!
CFStreamCreatePairWithSocketToHost(nil, h, UInt32(port), &readStream, &writeStream)
inputStream = readStream!.takeUnretainedValue()
outputStream = writeStream!.takeUnretainedValue()
inputStream = readStream!.takeRetainedValue()
outputStream = writeStream!.takeRetainedValue()

inputStream!.delegate = self
outputStream!.delegate = self
Expand Down Expand Up @@ -371,7 +354,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
if totalSize > 0 {
if validateResponse(buffer, bufferLen: totalSize) {
dispatch_async(queue,{
if let connectBlock = self.connectedBlock {
if let connectBlock = self.onConnect {
connectBlock()
}
self.delegate?.websocketDidConnect(self)
Expand Down Expand Up @@ -496,6 +479,10 @@ public class WebSocket : NSObject, NSStreamDelegate {
dataLength = UInt64(bytes[0].bigEndian)
offset += sizeof(UInt16)
}
if bufferLen < offset {
fragBuffer = NSData(bytes: buffer, length: bufferLen)
return
}
var len = dataLength
if dataLength > UInt64(bufferLen) {
len = UInt64(bufferLen-offset)
Expand All @@ -508,7 +495,13 @@ public class WebSocket : NSObject, NSStreamDelegate {
data = NSData(bytes: UnsafePointer<UInt8>((buffer+offset)), length: Int(len))
}
if receivedOpcode == OpCode.Pong.rawValue {
self.pongDelegate?.websocketDidReceivePong(self)
dispatch_async(queue,{
if let pongBlock = self.onPong {
pongBlock()
}
self.pongDelegate?.websocketDidReceivePong(self)
})

let step = Int(offset+numericCast(len))
let extra = bufferLen-step
if extra > 0 {
Expand Down Expand Up @@ -596,15 +589,15 @@ public class WebSocket : NSObject, NSStreamDelegate {
return false
}
dispatch_async(queue,{
if let textBlock = self.receivedTextBlock{
if let textBlock = self.onText {
textBlock(str! as! String)
}
self.delegate?.websocketDidReceiveMessage(self, text: str! as! String)
})
} else if response.code == .BinaryFrame {
let data = response.buffer! //local copy so it is perverse for writing
dispatch_async(queue,{
if let dataBlock = self.receivedDataBlock{
if let dataBlock = self.onData {
dataBlock(data)
}
self.delegate?.websocketDidReceiveData(self, data: data)
Expand Down Expand Up @@ -709,9 +702,10 @@ public class WebSocket : NSObject, NSStreamDelegate {

///used to preform the disconnect delegate
private func doDisconnect(error: NSError?) {
if self.isConnected {
if !self.didDisconnect {
dispatch_async(queue,{
if let disconnect = self.disconnectedBlock {
self.didDisconnect = true
if let disconnect = self.onDisconnect {
disconnect(error)
}
self.delegate?.websocketDidDisconnect(self, error: error)
Expand Down
Binary file not shown.
2 changes: 2 additions & 0 deletions examples/SimpleTest/SimpleTest/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class ViewController: UIViewController, WebSocketDelegate {
func websocketDidDisconnect(ws: WebSocket, error: NSError?) {
if let e = error {
println("websocket is disconnected: \(e.localizedDescription)")
} else {
println("websocket disconnected")
}
}

Expand Down

0 comments on commit 7b19d84

Please sign in to comment.