Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7a19c23e50 | |||
| f8b0efd58f | |||
| 0d4e21c42a | |||
| b60433b0cd | |||
| c34b561e69 | |||
| e182eb42d1 | |||
| ac3130fc70 |
+94
-80
@@ -11,13 +11,13 @@ import Foundation
|
||||
// MARK: Custom Extensions
|
||||
extension String {
|
||||
subscript (i: Int) -> String {
|
||||
return String(Array(self)[i])
|
||||
return String(Array(self.characters)[i])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: BayuexChannel Messages
|
||||
enum BayeuxChannel : Printable {
|
||||
enum BayeuxChannel : CustomStringConvertible {
|
||||
case HANDSHAKE_CHANNEL;
|
||||
case CONNECT_CHANNEL;
|
||||
case DISCONNECT_CHANNEL;
|
||||
@@ -41,17 +41,29 @@ enum BayeuxChannel : Printable {
|
||||
typealias ChannelSubscriptionBlock = (NSDictionary) -> Void
|
||||
|
||||
// MARK: FayeClientDelegate Protocol
|
||||
@objc protocol FayeClientDelegate{
|
||||
optional func messageReceived(messageDict: NSDictionary, channel: String)
|
||||
optional func connectedToServer()
|
||||
optional func disconnectedFromServer()
|
||||
optional func connectionFailed()
|
||||
optional func didSubscribeToChannel(channel:String)
|
||||
optional func didUnsubscribeFromChannel(channel:String)
|
||||
optional func subscriptionFailedWithError(error:String)
|
||||
optional func fayeClientError(error:NSError)
|
||||
protocol FayeClientDelegate: NSObjectProtocol{
|
||||
func messageReceived(messageDict: NSDictionary, channel: String)
|
||||
func connectedToServer()
|
||||
func disconnectedFromServer()
|
||||
func connectionFailed()
|
||||
func didSubscribeToChannel(channel:String)
|
||||
func didUnsubscribeFromChannel(channel:String)
|
||||
func subscriptionFailedWithError(error:String)
|
||||
func fayeClientError(error:NSError)
|
||||
}
|
||||
|
||||
extension FayeClientDelegate {
|
||||
func messageReceived(messageDict: NSDictionary, channel: String){}
|
||||
func connectedToServer(){}
|
||||
func disconnectedFromServer(){}
|
||||
func connectionFailed(){}
|
||||
func didSubscribeToChannel(channel:String){}
|
||||
func didUnsubscribeFromChannel(channel:String){}
|
||||
func subscriptionFailedWithError(error:String){}
|
||||
func fayeClientError(error:NSError){}
|
||||
}
|
||||
|
||||
|
||||
protocol Transport{
|
||||
func writeString(aString:String)
|
||||
func openConnection()
|
||||
@@ -102,29 +114,29 @@ public class WebsocketTransport: Transport, WebSocketDelegate {
|
||||
|
||||
// MARK: Websocket Delegate
|
||||
public func websocketDidConnect(socket: WebSocket) {
|
||||
println("websocket is connected")
|
||||
print("websocket is connected")
|
||||
self.delegate?.didConnect()
|
||||
}
|
||||
|
||||
public func websocketDidDisconnect(socket: WebSocket, error: NSError?) {
|
||||
|
||||
if(error == nil){
|
||||
println("websocket lost connection!")
|
||||
print("websocket lost connection!")
|
||||
self.delegate?.didDisconnect()
|
||||
}else{
|
||||
println("websocket is disconnected: \(error!.localizedDescription)")
|
||||
print("websocket is disconnected: \(error!.localizedDescription)")
|
||||
self.delegate?.didFailConenction(error)
|
||||
}
|
||||
}
|
||||
|
||||
public func websocketDidReceiveMessage(socket: WebSocket, text: String) {
|
||||
println("got some text: \(text)")
|
||||
print("got some text: \(text)")
|
||||
self.delegate?.didReceiveMessage(text)
|
||||
}
|
||||
|
||||
// MARK: TODO
|
||||
public func websocketDidReceiveData(socket: WebSocket, data: NSData) {
|
||||
println("got some data: \(data.length)")
|
||||
print("got some data: \(data.length)")
|
||||
//self.socket.writeData(data)
|
||||
}
|
||||
}
|
||||
@@ -206,6 +218,8 @@ class FayeClient : TransportDelegate {
|
||||
self.queuedSubscriptions.removeObject(channel)
|
||||
self.unsubscribe(channel)
|
||||
self.channelSubscriptionBlocks[channel] = nil;
|
||||
self.openSubscriptions.removeObject(channel)
|
||||
self.pendingSubscriptions.removeObject(channel)
|
||||
}
|
||||
|
||||
func isSubscribedToChannel(channel:String) -> (Bool){
|
||||
@@ -219,38 +233,38 @@ class FayeClient : TransportDelegate {
|
||||
|
||||
|
||||
// MARK: Transport Delegate
|
||||
private extension FayeClient {
|
||||
internal func didConnect() {
|
||||
println("Transport websocket is connected")
|
||||
extension FayeClient {
|
||||
func didConnect() {
|
||||
print("Transport websocket is connected")
|
||||
self.connectionInitiated = false;
|
||||
self.handshake()
|
||||
}
|
||||
|
||||
internal func didDisconnect() {
|
||||
println("Transport websocket lost connection!")
|
||||
self.delegate?.disconnectedFromServer?()
|
||||
func didDisconnect() {
|
||||
print("Transport websocket lost connection!")
|
||||
self.delegate?.disconnectedFromServer()
|
||||
self.connectionInitiated = false
|
||||
self.fayeConnected = false
|
||||
}
|
||||
|
||||
internal func didFailConenction(error: NSError?) {
|
||||
println("Transport websocket is disconnected: \(error!.localizedDescription)")
|
||||
self.delegate?.connectionFailed?()
|
||||
func didFailConenction(error: NSError?) {
|
||||
print("Transport websocket is disconnected: \(error!.localizedDescription)")
|
||||
self.delegate?.connectionFailed()
|
||||
self.connectionInitiated = false
|
||||
self.fayeConnected = false
|
||||
}
|
||||
|
||||
internal func didWriteError(error: NSError?) {
|
||||
func didWriteError(error: NSError?) {
|
||||
if(error == nil){
|
||||
println("Transport websocket write failed: ERROR IS NIL!")
|
||||
print("Transport websocket write failed: ERROR IS NIL!")
|
||||
}else{
|
||||
println("Transport websocket write failed: \(error!.localizedDescription)")
|
||||
self.delegate?.fayeClientError?(error!)
|
||||
print("Transport websocket write failed: \(error!.localizedDescription)")
|
||||
self.delegate?.fayeClientError(error!)
|
||||
}
|
||||
}
|
||||
|
||||
internal func didReceiveMessage(text: String) {
|
||||
println("Transport got some text: \(text)")
|
||||
func didReceiveMessage(text: String) {
|
||||
print("Transport got some text: \(text)")
|
||||
self.receive(text)
|
||||
}
|
||||
|
||||
@@ -266,11 +280,11 @@ private extension FayeClient {
|
||||
switch(channel)
|
||||
{
|
||||
case BayeuxChannel.HANDSHAKE_CHANNEL.description:
|
||||
println("HANDSHAKE_CHANNEL")
|
||||
print("HANDSHAKE_CHANNEL")
|
||||
self.fayeClientId = messageDict["clientId"].stringValue
|
||||
if(messageDict["successful"].int == 1){
|
||||
|
||||
self.delegate?.connectedToServer?()
|
||||
self.delegate?.connectedToServer()
|
||||
self.fayeConnected = true;
|
||||
self.connect()
|
||||
self.subscribeQueuedSubscriptions()
|
||||
@@ -280,7 +294,7 @@ private extension FayeClient {
|
||||
}
|
||||
|
||||
case BayeuxChannel.CONNECT_CHANNEL.description:
|
||||
println("CONNECT_CHANNEL")
|
||||
print("CONNECT_CHANNEL")
|
||||
if(messageDict["successful"].int == 1){
|
||||
self.fayeConnected = true;
|
||||
self.connect()
|
||||
@@ -288,16 +302,16 @@ private extension FayeClient {
|
||||
// OOPS
|
||||
}
|
||||
case BayeuxChannel.DISCONNECT_CHANNEL.description:
|
||||
println("DISCONNECT_CHANNEL")
|
||||
print("DISCONNECT_CHANNEL")
|
||||
if(messageDict["successful"].int == 1){
|
||||
self.fayeConnected = false;
|
||||
self.transport?.closeConnection()
|
||||
self.delegate?.disconnectedFromServer?()
|
||||
self.delegate?.disconnectedFromServer()
|
||||
}else{
|
||||
// OOPS
|
||||
}
|
||||
case BayeuxChannel.SUBSCRIBE_CHANNEL.description:
|
||||
println("SUBSCRIBE_CHANNEL")
|
||||
print("SUBSCRIBE_CHANNEL")
|
||||
|
||||
let success = messageJSON[0]["successful"].int
|
||||
|
||||
@@ -305,49 +319,49 @@ private extension FayeClient {
|
||||
if let subscription = messageJSON[0]["subscription"].string{
|
||||
self.pendingSubscriptions.removeObject(subscription)
|
||||
self.openSubscriptions.addObject(subscription)
|
||||
self.delegate?.didSubscribeToChannel?(subscription)
|
||||
self.delegate?.didSubscribeToChannel(subscription)
|
||||
}else{
|
||||
println("Missing subscription for Subscribe")
|
||||
print("Missing subscription for Subscribe")
|
||||
}
|
||||
}else{
|
||||
// Subscribe Failed
|
||||
if let error = messageJSON[0]["error"].string{
|
||||
self.delegate?.subscriptionFailedWithError?(error)
|
||||
self.delegate?.subscriptionFailedWithError(error)
|
||||
}
|
||||
}
|
||||
case BayeuxChannel.UNSUBSCRIBE_CHANNEL.description:
|
||||
println("UNSUBSCRIBE_CHANNEL")
|
||||
print("UNSUBSCRIBE_CHANNEL")
|
||||
|
||||
if let subscription = messageJSON[0]["subscription"].string{
|
||||
self.openSubscriptions.removeObject(subscription)
|
||||
self.delegate?.didUnsubscribeFromChannel?(subscription)
|
||||
self.delegate?.didUnsubscribeFromChannel(subscription)
|
||||
}else{
|
||||
println("Missing subscription for Unsubscribe")
|
||||
print("Missing subscription for Unsubscribe")
|
||||
}
|
||||
default:
|
||||
if(self.isSubscribedToChannel(channel)){
|
||||
println("New Message on \(channel)")
|
||||
print("New Message on \(channel)")
|
||||
|
||||
if(messageJSON[0]["data"] != JSON.nullJSON){
|
||||
if(messageJSON[0]["data"] != JSON.null){
|
||||
// Call channel subscription block if there is one
|
||||
let data: AnyObject = messageJSON[0]["data"].object
|
||||
if let channelBlock = self.channelSubscriptionBlocks[channel]{
|
||||
channelBlock(data as! NSDictionary)
|
||||
}else{
|
||||
self.delegate?.messageReceived?(data as! NSDictionary, channel: channel)
|
||||
self.delegate?.messageReceived(data as! NSDictionary, channel: channel)
|
||||
}
|
||||
|
||||
}else{
|
||||
println("For some reason data is nil, maybe double posting?!")
|
||||
print("For some reason data is nil, maybe double posting?!")
|
||||
}
|
||||
|
||||
}else{
|
||||
println("weird channel")
|
||||
print("weird channel")
|
||||
}
|
||||
}
|
||||
|
||||
}else{
|
||||
println("Missing channel")
|
||||
print("Missing channel")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,7 +375,7 @@ private extension FayeClient {
|
||||
// "minimumVersion": "1.0beta",
|
||||
// "supportedConnectionTypes": ["long-polling", "callback-polling", "iframe", "websocket]
|
||||
func handshake() {
|
||||
var connTypes:NSArray = ["long-polling", "callback-polling", "iframe", "websocket"]
|
||||
let connTypes:NSArray = ["long-polling", "callback-polling", "iframe", "websocket"]
|
||||
var dict = [String: AnyObject]()
|
||||
dict["channel"] = BayeuxChannel.HANDSHAKE_CHANNEL.description
|
||||
dict["version"] = "1.0"
|
||||
@@ -377,7 +391,7 @@ private extension FayeClient {
|
||||
// "clientId": "Un1q31d3nt1f13r",
|
||||
// "connectionType": "long-polling"
|
||||
func connect(){
|
||||
var dict:[String:AnyObject] = ["channel": BayeuxChannel.CONNECT_CHANNEL.description, "clientId": self.fayeClientId!, "connectionType": "websocket"]
|
||||
let dict:[String:AnyObject] = ["channel": BayeuxChannel.CONNECT_CHANNEL.description, "clientId": self.fayeClientId!, "connectionType": "websocket"]
|
||||
|
||||
let string = JSONStringify(dict)
|
||||
self.transport?.writeString(string)
|
||||
@@ -387,7 +401,7 @@ private extension FayeClient {
|
||||
// "channel": "/meta/disconnect",
|
||||
// "clientId": "Un1q31d3nt1f13r"
|
||||
func disconnect(){
|
||||
var dict:[String:AnyObject] = ["channel": BayeuxChannel.DISCONNECT_CHANNEL.description, "clientId": self.fayeClientId!, "connectionType": "websocket"]
|
||||
let dict:[String:AnyObject] = ["channel": BayeuxChannel.DISCONNECT_CHANNEL.description, "clientId": self.fayeClientId!, "connectionType": "websocket"]
|
||||
let string = JSONStringify(dict)
|
||||
self.transport?.writeString(string)
|
||||
}
|
||||
@@ -399,7 +413,7 @@ private extension FayeClient {
|
||||
// "subscription": "/foo/**"
|
||||
// }
|
||||
func subscribe(channel:String){
|
||||
var dict:[String:AnyObject] = ["channel": BayeuxChannel.SUBSCRIBE_CHANNEL.description, "clientId": self.fayeClientId!, "subscription": channel]
|
||||
let dict:[String:AnyObject] = ["channel": BayeuxChannel.SUBSCRIBE_CHANNEL.description, "clientId": self.fayeClientId!, "subscription": channel]
|
||||
let string = JSONStringify(dict)
|
||||
self.transport?.writeString(string)
|
||||
self.pendingSubscriptions.addObject(channel)
|
||||
@@ -412,9 +426,11 @@ private extension FayeClient {
|
||||
// "subscription": "/foo/**"
|
||||
// }
|
||||
func unsubscribe(channel:String){
|
||||
var dict:[String:AnyObject] = ["channel": BayeuxChannel.UNSUBSCRIBE_CHANNEL.description, "clientId": self.fayeClientId!, "subscription": channel]
|
||||
let string = JSONStringify(dict)
|
||||
self.transport?.writeString(string)
|
||||
if let clientId = self.fayeClientId {
|
||||
let dict:[String:AnyObject] = ["channel": BayeuxChannel.UNSUBSCRIBE_CHANNEL.description, "clientId": clientId, "subscription": channel]
|
||||
let string = JSONStringify(dict)
|
||||
self.transport?.writeString(string)
|
||||
}
|
||||
}
|
||||
|
||||
// Bayeux Publish
|
||||
@@ -426,10 +442,10 @@ private extension FayeClient {
|
||||
// }
|
||||
func publish(data:[String:AnyObject], channel:String){
|
||||
if(self.fayeConnected == true){
|
||||
var dict:[String:AnyObject] = ["channel": channel, "clientId": self.fayeClientId!, "id": self.nextMessageId(), "data": data]
|
||||
let dict:[String:AnyObject] = ["channel": channel, "clientId": self.fayeClientId!, "id": self.nextMessageId(), "data": data]
|
||||
|
||||
var string = JSONStringify(dict)
|
||||
println("THIS IS THE PUBSLISH STRING: \(string)")
|
||||
let string = JSONStringify(dict)
|
||||
print("THIS IS THE PUBSLISH STRING: \(string)")
|
||||
self.transport?.writeString(string)
|
||||
}else{
|
||||
// Faye is not connected
|
||||
@@ -453,21 +469,21 @@ private extension FayeClient {
|
||||
|
||||
func send(message: NSDictionary){
|
||||
// Parse JSON
|
||||
var writeError:NSError?
|
||||
var jsonData:NSData = NSJSONSerialization.dataWithJSONObject(message, options:nil, error: &writeError)!
|
||||
|
||||
if(writeError == nil){
|
||||
println("COuldn't parse json")
|
||||
}else{
|
||||
var jsonString:NSString = NSString(data: jsonData, encoding:NSUTF8StringEncoding)!
|
||||
do {
|
||||
let jsonData:NSData = try! NSJSONSerialization.dataWithJSONObject(message, options:[])
|
||||
let jsonString:NSString = NSString(data: jsonData, encoding:NSUTF8StringEncoding)!
|
||||
self.transport?.writeString(jsonString as String)
|
||||
} catch let error as NSError {
|
||||
print("[Send Message] Couldn't Parse JSON: \(error.localizedDescription)")
|
||||
} catch {
|
||||
print("[Send Message]: Unknown error")
|
||||
}
|
||||
}
|
||||
|
||||
func receive(message: String){
|
||||
// Parse JSON
|
||||
var jsonData = message.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)
|
||||
var json = JSON(data: jsonData!)
|
||||
let jsonData = message.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)
|
||||
let json = JSON(data: jsonData!)
|
||||
self.parseFayeMessage(json)
|
||||
}
|
||||
|
||||
@@ -477,8 +493,8 @@ private extension FayeClient {
|
||||
if(self.messageNumber >= UINT32_MAX){
|
||||
messageNumber = 0
|
||||
}
|
||||
var str = "\(self.messageNumber)"
|
||||
println("Original: \(str)")
|
||||
let str = "\(self.messageNumber)"
|
||||
print("Original: \(str)")
|
||||
|
||||
// UTF 8 str from original
|
||||
// NSData! type returned (optional)
|
||||
@@ -489,7 +505,7 @@ private extension FayeClient {
|
||||
// Notice the unwrapping given the NSData! optional
|
||||
// NSString! returned (optional)
|
||||
let base64Encoded = utf8str?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions())
|
||||
println("Encoded: \(base64Encoded)")
|
||||
print("Encoded: \(base64Encoded)")
|
||||
|
||||
// Base64 Decode (go back the other way)
|
||||
// Notice the unwrapping given the NSString! optional
|
||||
@@ -498,22 +514,20 @@ private extension FayeClient {
|
||||
|
||||
// Convert back to a string
|
||||
let base64Decoded = NSString(data: data!, encoding: NSUTF8StringEncoding)
|
||||
println("Decoded: \(base64Decoded)")
|
||||
print("Decoded: \(base64Decoded)")
|
||||
|
||||
return base64Decoded! as String
|
||||
}
|
||||
|
||||
// JSON Helpers
|
||||
func JSONStringify(jsonObj: AnyObject) -> String {
|
||||
var e: NSError?
|
||||
let jsonData: NSData! = NSJSONSerialization.dataWithJSONObject(
|
||||
jsonObj,
|
||||
options: NSJSONWritingOptions(0),
|
||||
error: &e)
|
||||
if e != nil {
|
||||
return ""
|
||||
} else {
|
||||
do {
|
||||
let jsonData:NSData = try! NSJSONSerialization.dataWithJSONObject(jsonObj, options:NSJSONWritingOptions(rawValue: 0))
|
||||
return NSString(data: jsonData, encoding: NSUTF8StringEncoding)! as String
|
||||
} catch let error as NSError {
|
||||
print("[JSONStringify] Couldn't Parse JSON: \(error.localizedDescription)")
|
||||
} catch {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,434 +0,0 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
AF021BFA19DC58FC0059EB60 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF021BF919DC58FC0059EB60 /* AppDelegate.swift */; };
|
||||
AF021BFC19DC58FC0059EB60 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF021BFB19DC58FC0059EB60 /* ViewController.swift */; };
|
||||
AF021BFF19DC58FC0059EB60 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AF021BFD19DC58FC0059EB60 /* Main.storyboard */; };
|
||||
AF021C0119DC58FC0059EB60 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AF021C0019DC58FC0059EB60 /* Images.xcassets */; };
|
||||
AF021C0419DC58FC0059EB60 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = AF021C0219DC58FC0059EB60 /* LaunchScreen.xib */; };
|
||||
AF021C1019DC58FC0059EB60 /* FayeSwiftDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF021C0F19DC58FC0059EB60 /* FayeSwiftDemoTests.swift */; };
|
||||
AF021C1B19DC591E0059EB60 /* FayeClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF021C1919DC591E0059EB60 /* FayeClient.swift */; };
|
||||
AF021C2019DC5B7D0059EB60 /* Websocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF021C1E19DC5B7D0059EB60 /* Websocket.swift */; };
|
||||
AF021C2119DC5B7D0059EB60 /* SwiftyJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF021C1F19DC5B7D0059EB60 /* SwiftyJSON.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
AF021C0A19DC58FC0059EB60 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = AF021BEC19DC58FC0059EB60 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = AF021BF319DC58FC0059EB60;
|
||||
remoteInfo = FayeSwiftDemo;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
AF021BF419DC58FC0059EB60 /* FayeSwiftDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FayeSwiftDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
AF021BF819DC58FC0059EB60 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
AF021BF919DC58FC0059EB60 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
AF021BFB19DC58FC0059EB60 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
||||
AF021BFE19DC58FC0059EB60 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
AF021C0019DC58FC0059EB60 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
|
||||
AF021C0319DC58FC0059EB60 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
|
||||
AF021C0919DC58FC0059EB60 /* FayeSwiftDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FayeSwiftDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
AF021C0E19DC58FC0059EB60 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
AF021C0F19DC58FC0059EB60 /* FayeSwiftDemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FayeSwiftDemoTests.swift; sourceTree = "<group>"; };
|
||||
AF021C1919DC591E0059EB60 /* FayeClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FayeClient.swift; path = ../FayeClient.swift; sourceTree = "<group>"; };
|
||||
AF021C1E19DC5B7D0059EB60 /* Websocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Websocket.swift; path = ../lib/Websocket.swift; sourceTree = "<group>"; };
|
||||
AF021C1F19DC5B7D0059EB60 /* SwiftyJSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftyJSON.swift; path = ../lib/SwiftyJSON.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
AF021BF119DC58FC0059EB60 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
AF021C0619DC58FC0059EB60 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
AF021BEB19DC58FC0059EB60 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AF021C1E19DC5B7D0059EB60 /* Websocket.swift */,
|
||||
AF021C1F19DC5B7D0059EB60 /* SwiftyJSON.swift */,
|
||||
AF021C1919DC591E0059EB60 /* FayeClient.swift */,
|
||||
AF021BF619DC58FC0059EB60 /* FayeSwiftDemo */,
|
||||
AF021C0C19DC58FC0059EB60 /* FayeSwiftDemoTests */,
|
||||
AF021BF519DC58FC0059EB60 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
AF021BF519DC58FC0059EB60 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AF021BF419DC58FC0059EB60 /* FayeSwiftDemo.app */,
|
||||
AF021C0919DC58FC0059EB60 /* FayeSwiftDemoTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
AF021BF619DC58FC0059EB60 /* FayeSwiftDemo */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AF021BF919DC58FC0059EB60 /* AppDelegate.swift */,
|
||||
AF021BFB19DC58FC0059EB60 /* ViewController.swift */,
|
||||
AF021BFD19DC58FC0059EB60 /* Main.storyboard */,
|
||||
AF021C0019DC58FC0059EB60 /* Images.xcassets */,
|
||||
AF021C0219DC58FC0059EB60 /* LaunchScreen.xib */,
|
||||
AF021BF719DC58FC0059EB60 /* Supporting Files */,
|
||||
);
|
||||
path = FayeSwiftDemo;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
AF021BF719DC58FC0059EB60 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AF021BF819DC58FC0059EB60 /* Info.plist */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
AF021C0C19DC58FC0059EB60 /* FayeSwiftDemoTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AF021C0F19DC58FC0059EB60 /* FayeSwiftDemoTests.swift */,
|
||||
AF021C0D19DC58FC0059EB60 /* Supporting Files */,
|
||||
);
|
||||
path = FayeSwiftDemoTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
AF021C0D19DC58FC0059EB60 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AF021C0E19DC58FC0059EB60 /* Info.plist */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
AF021BF319DC58FC0059EB60 /* FayeSwiftDemo */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = AF021C1319DC58FC0059EB60 /* Build configuration list for PBXNativeTarget "FayeSwiftDemo" */;
|
||||
buildPhases = (
|
||||
AF021BF019DC58FC0059EB60 /* Sources */,
|
||||
AF021BF119DC58FC0059EB60 /* Frameworks */,
|
||||
AF021BF219DC58FC0059EB60 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = FayeSwiftDemo;
|
||||
productName = FayeSwiftDemo;
|
||||
productReference = AF021BF419DC58FC0059EB60 /* FayeSwiftDemo.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
AF021C0819DC58FC0059EB60 /* FayeSwiftDemoTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = AF021C1619DC58FC0059EB60 /* Build configuration list for PBXNativeTarget "FayeSwiftDemoTests" */;
|
||||
buildPhases = (
|
||||
AF021C0519DC58FC0059EB60 /* Sources */,
|
||||
AF021C0619DC58FC0059EB60 /* Frameworks */,
|
||||
AF021C0719DC58FC0059EB60 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
AF021C0B19DC58FC0059EB60 /* PBXTargetDependency */,
|
||||
);
|
||||
name = FayeSwiftDemoTests;
|
||||
productName = FayeSwiftDemoTests;
|
||||
productReference = AF021C0919DC58FC0059EB60 /* FayeSwiftDemoTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
AF021BEC19DC58FC0059EB60 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0600;
|
||||
ORGANIZATIONNAME = "Haris Amin";
|
||||
TargetAttributes = {
|
||||
AF021BF319DC58FC0059EB60 = {
|
||||
CreatedOnToolsVersion = 6.0.1;
|
||||
};
|
||||
AF021C0819DC58FC0059EB60 = {
|
||||
CreatedOnToolsVersion = 6.0.1;
|
||||
TestTargetID = AF021BF319DC58FC0059EB60;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = AF021BEF19DC58FC0059EB60 /* Build configuration list for PBXProject "FayeSwiftDemo" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = AF021BEB19DC58FC0059EB60;
|
||||
productRefGroup = AF021BF519DC58FC0059EB60 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
AF021BF319DC58FC0059EB60 /* FayeSwiftDemo */,
|
||||
AF021C0819DC58FC0059EB60 /* FayeSwiftDemoTests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
AF021BF219DC58FC0059EB60 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
AF021BFF19DC58FC0059EB60 /* Main.storyboard in Resources */,
|
||||
AF021C0419DC58FC0059EB60 /* LaunchScreen.xib in Resources */,
|
||||
AF021C0119DC58FC0059EB60 /* Images.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
AF021C0719DC58FC0059EB60 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
AF021BF019DC58FC0059EB60 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
AF021BFC19DC58FC0059EB60 /* ViewController.swift in Sources */,
|
||||
AF021C2019DC5B7D0059EB60 /* Websocket.swift in Sources */,
|
||||
AF021BFA19DC58FC0059EB60 /* AppDelegate.swift in Sources */,
|
||||
AF021C2119DC5B7D0059EB60 /* SwiftyJSON.swift in Sources */,
|
||||
AF021C1B19DC591E0059EB60 /* FayeClient.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
AF021C0519DC58FC0059EB60 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
AF021C1019DC58FC0059EB60 /* FayeSwiftDemoTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
AF021C0B19DC58FC0059EB60 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = AF021BF319DC58FC0059EB60 /* FayeSwiftDemo */;
|
||||
targetProxy = AF021C0A19DC58FC0059EB60 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
AF021BFD19DC58FC0059EB60 /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
AF021BFE19DC58FC0059EB60 /* Base */,
|
||||
);
|
||||
name = Main.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
AF021C0219DC58FC0059EB60 /* LaunchScreen.xib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
AF021C0319DC58FC0059EB60 /* Base */,
|
||||
);
|
||||
name = LaunchScreen.xib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
AF021C1119DC58FC0059EB60 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
AF021C1219DC58FC0059EB60 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
AF021C1419DC58FC0059EB60 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
INFOPLIST_FILE = FayeSwiftDemo/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
AF021C1519DC58FC0059EB60 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
INFOPLIST_FILE = FayeSwiftDemo/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
AF021C1719DC58FC0059EB60 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
INFOPLIST_FILE = FayeSwiftDemoTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FayeSwiftDemo.app/FayeSwiftDemo";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
AF021C1819DC58FC0059EB60 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
"$(inherited)",
|
||||
);
|
||||
INFOPLIST_FILE = FayeSwiftDemoTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FayeSwiftDemo.app/FayeSwiftDemo";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
AF021BEF19DC58FC0059EB60 /* Build configuration list for PBXProject "FayeSwiftDemo" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
AF021C1119DC58FC0059EB60 /* Debug */,
|
||||
AF021C1219DC58FC0059EB60 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
AF021C1319DC58FC0059EB60 /* Build configuration list for PBXNativeTarget "FayeSwiftDemo" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
AF021C1419DC58FC0059EB60 /* Debug */,
|
||||
AF021C1519DC58FC0059EB60 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
};
|
||||
AF021C1619DC58FC0059EB60 /* Build configuration list for PBXNativeTarget "FayeSwiftDemoTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
AF021C1719DC58FC0059EB60 /* Debug */,
|
||||
AF021C1819DC58FC0059EB60 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = AF021BEC19DC58FC0059EB60 /* Project object */;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
//
|
||||
// AppDelegate.swift
|
||||
// FayeSwiftDemo
|
||||
//
|
||||
// Created by Haris Amin on 10/1/14.
|
||||
// Copyright (c) 2014 Haris Amin. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
|
||||
|
||||
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
|
||||
// Override point for customization after application launch.
|
||||
return true
|
||||
}
|
||||
|
||||
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) {
|
||||
// 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) {
|
||||
// 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) {
|
||||
// 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) {
|
||||
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6214" systemVersion="14A314h" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6207"/>
|
||||
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Copyright (c) 2014 Haris Amin. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
|
||||
<rect key="frame" x="20" y="439" width="441" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="FayeSwiftDemo" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
|
||||
<rect key="frame" x="20" y="140" width="441" height="43"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
|
||||
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
|
||||
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
|
||||
<constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
|
||||
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
|
||||
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
|
||||
</constraints>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<point key="canvasLocation" x="548" y="455"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -1,59 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6245" systemVersion="13F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="vXZ-lx-hvc">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6238"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="ufC-wZ-h7g">
|
||||
<objects>
|
||||
<viewController id="vXZ-lx-hvc" customClass="ViewController" customModule="FayeSwiftDemo" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="jyV-Pf-zRb"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="2fi-mo-0CV"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="kh9-bI-dsS">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="A5l-SM-JaF">
|
||||
<rect key="frame" x="178" y="176" width="244" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="244" id="a2K-B4-uYz"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" returnKeyType="send"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="vXZ-lx-hvc" id="2is-GC-Xhp"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="tgX-Ab-r7g">
|
||||
<rect key="frame" x="180" y="283" width="240" height="128"/>
|
||||
<color key="backgroundColor" red="0.2039215714" green="0.28627452250000002" blue="0.36862745879999997" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="240" id="pb6-Yk-ZTr"/>
|
||||
</constraints>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="2fi-mo-0CV" firstAttribute="top" secondItem="tgX-Ab-r7g" secondAttribute="bottom" constant="189" id="7rd-Hn-jxT"/>
|
||||
<constraint firstItem="A5l-SM-JaF" firstAttribute="top" secondItem="jyV-Pf-zRb" secondAttribute="bottom" constant="156" id="A1O-zM-am6"/>
|
||||
<constraint firstItem="A5l-SM-JaF" firstAttribute="centerX" secondItem="tgX-Ab-r7g" secondAttribute="centerX" id="TSF-Nu-Xdo"/>
|
||||
<constraint firstItem="tgX-Ab-r7g" firstAttribute="top" secondItem="A5l-SM-JaF" secondAttribute="bottom" constant="77" id="hKI-Ji-3F4"/>
|
||||
<constraint firstItem="A5l-SM-JaF" firstAttribute="centerX" secondItem="kh9-bI-dsS" secondAttribute="centerX" id="nl1-i1-8kd"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="textField" destination="A5l-SM-JaF" id="BAk-ys-uXO"/>
|
||||
<outlet property="textView" destination="tgX-Ab-r7g" id="TiA-OI-MNC"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="x5A-6p-PRh" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>HarisAmin.$(PRODUCT_NAME:rfc1034identifier)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,76 +0,0 @@
|
||||
//
|
||||
// ViewController.swift
|
||||
// FayeSwiftDemo
|
||||
//
|
||||
// Created by Haris Amin on 10/1/14.
|
||||
// Copyright (c) 2014 Haris Amin. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class ViewController: UIViewController, UITextFieldDelegate, FayeClientDelegate {
|
||||
@IBOutlet weak var textField: UITextField!
|
||||
@IBOutlet weak var textView: UITextView!
|
||||
|
||||
let client:FayeClient = FayeClient(aFayeURLString: "ws://localhost:5222/faye", channel: "/cool")
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
// Do any additional setup after loading the view, typically from a nib.
|
||||
client.delegate = self;
|
||||
client.connectToServer()
|
||||
|
||||
let channelBlock:ChannelSubscriptionBlock = {(messageDict) -> Void in
|
||||
let text: AnyObject? = messageDict["text"]
|
||||
println("Here is the Block message: \(text)")
|
||||
}
|
||||
client.subscribeToChannel("/awesome", block: channelBlock)
|
||||
}
|
||||
|
||||
override func didReceiveMemoryWarning() {
|
||||
super.didReceiveMemoryWarning()
|
||||
// Dispose of any resources that can be recreated.
|
||||
}
|
||||
|
||||
func textFieldShouldReturn(textField: UITextField) -> Bool {
|
||||
client.sendMessage(["text": textField.text], channel: "/cool")
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
func connectedToServer() {
|
||||
println("Connected to Faye server")
|
||||
}
|
||||
|
||||
func connectionFailed() {
|
||||
println("Failed to connect to Faye server!")
|
||||
}
|
||||
|
||||
func disconnectedFromServer() {
|
||||
println("Disconnected from Faye server")
|
||||
}
|
||||
|
||||
func didSubscribeToChannel(channel: String) {
|
||||
println("subscribed to channel \(channel)")
|
||||
}
|
||||
|
||||
func didUnsubscribeFromChannel(channel: String) {
|
||||
println("UNsubscribed from channel \(channel)")
|
||||
}
|
||||
|
||||
func subscriptionFailedWithError(error: String) {
|
||||
println("SUBSCRIPTION FAILED!!!!")
|
||||
}
|
||||
|
||||
func messageReceived(messageDict: NSDictionary, channel: String) {
|
||||
let text: AnyObject? = messageDict["text"]
|
||||
println("Here is the message: \(text)")
|
||||
|
||||
|
||||
// self.client.subscribeToChannel("/newchannelbaby")
|
||||
// self.client.unsubscribeFromChannel(channel)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
//
|
||||
// FayeSwiftDemoTests.swift
|
||||
// FayeSwiftDemoTests
|
||||
//
|
||||
// Created by Haris Amin on 10/1/14.
|
||||
// Copyright (c) 2014 Haris Amin. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import XCTest
|
||||
|
||||
class FayeSwiftDemoTests: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testExample() {
|
||||
// This is an example of a functional test case.
|
||||
XCTAssert(true, "Pass")
|
||||
}
|
||||
|
||||
func testPerformanceExample() {
|
||||
// This is an example of a performance test case.
|
||||
self.measureBlock() {
|
||||
// Put the code you want to measure the time of here.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>HarisAmin.$(PRODUCT_NAME:rfc1034identifier)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Haris Amin
|
||||
Copyright for portions of project Foo are held by Haris Amin, 2014 as part of project FayeSwift.
|
||||
All other copyright for project Foo are held by Darcy Laycock, 2016.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -1,10 +1,6 @@
|
||||
# FayeSwift
|
||||
|
||||
[](https://gitter.im/hamin/FayeSwift?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||

|
||||
|
||||
|
||||
A simple Swift client library for the [Faye](http://faye.jcoglan.com/) publish-subscribe messaging server. FayeObjC is implemented atop the [Starscream](https://github.com/daltoniam/starscream) Swift web socket library and will work on both Mac (pending Xcode 6 Swift update) and iPhone projects.
|
||||
|
||||
It was heavily inspired by the Objective-C client found here: [FayeObjc](https://github.com/pcrawfor/FayeObjC)
|
||||
@@ -13,7 +9,6 @@ It was heavily inspired by the Objective-C client found here: [FayeObjc](https:/
|
||||
|
||||
### Installation
|
||||
|
||||
For now, add the following files to your project: `FayeClient.swift`, `Websocket.swift`, and `SwiftyJSON.swift`.
|
||||
|
||||
### Initializing Client
|
||||
|
||||
@@ -127,26 +122,9 @@ You can call sendMessage to send a dictionary object to a channel
|
||||
client.sendMessage(["text": textField.text], channel: "/cool")
|
||||
```
|
||||
|
||||
## Example Server
|
||||
|
||||
There is a sample faye server using the NodeJS Faye library. If you have NodeJS installed just run the following commands to install the package:
|
||||
|
||||
```javascript
|
||||
npm install
|
||||
```
|
||||
|
||||
And then you can start the Faye server like so:
|
||||
|
||||
```javascript
|
||||
node faye_server.js
|
||||
```
|
||||
## Example Project
|
||||
|
||||
Check out the FayeSwiftDemo project to see how to setup a simple connection to a Faye server.
|
||||
|
||||
## Requirements
|
||||
|
||||
FayeSwift requires at least iOS 7/OSX 10.10 or above.
|
||||
FayeSwift requires at least iOS8 / tvOS9.
|
||||
|
||||
## TODOs
|
||||
|
||||
@@ -159,9 +137,6 @@ FayeSwift requires at least iOS 7/OSX 10.10 or above.
|
||||
|
||||
## License
|
||||
|
||||
BayeuxSwift is licensed under the MIT License and is a derivative of the [FayeSwift]() project.
|
||||
|
||||
FayeSwift is licensed under the MIT License.
|
||||
|
||||
## Libraries
|
||||
|
||||
* [Starscream](https://github.com/daltoniam)
|
||||
* [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON)
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
var http = require('http'),
|
||||
faye = require('faye');
|
||||
|
||||
|
||||
faye.Logging.logLevel = 'debug';
|
||||
|
||||
var bayeux = new faye.NodeAdapter({
|
||||
mount: '/faye',
|
||||
timeout: 45
|
||||
});
|
||||
|
||||
// Handle non-Bayeux requests
|
||||
var server = http.createServer(function(request, response) {
|
||||
response.writeHead(200, {'Content-Type': 'text/plain'});
|
||||
response.write('Non-Bayeux request');
|
||||
response.end();
|
||||
});
|
||||
|
||||
serverLog = {
|
||||
incoming: function(message, callback) {
|
||||
console.log("SOMETTHING HAPPENDED!");
|
||||
if (message.channel === '/meta/subscribe') {
|
||||
logWithTimeStamp("CLIENT SUBSCRIBED Client ID: " + message.clientId);
|
||||
}
|
||||
if (message.channel.match(/\/users\/*/)) {
|
||||
logWithTimeStamp("USER MESSAGE ON CHANNEL: " + message.channel);
|
||||
}
|
||||
return callback(message);
|
||||
}
|
||||
};
|
||||
logWithTimeStamp = function(logMessage) {
|
||||
var timestampedMessage;
|
||||
timestampedMessage = "" + (Date()) + " | " + logMessage;
|
||||
return console.log(timestampedMessage);
|
||||
};
|
||||
|
||||
|
||||
bayeux.bind('handshake', function(client_id) {
|
||||
console.log("[handshake] - client: '"+ client_id +"'");
|
||||
});
|
||||
|
||||
bayeux.bind('subscribe', function(client_id, channel) {
|
||||
console.log("[subscribe] - client: '"+ client_id +"', channel: '"+ channel +"'");
|
||||
});
|
||||
|
||||
bayeux.bind('unsubscribe', function(client_id, channel) {
|
||||
console.log("[unsubscribe] - client: '"+ client_id +"', channel: '"+ channel +"'");
|
||||
});
|
||||
|
||||
bayeux.bind('publish', function(client_id, channel, data) {
|
||||
console.log("[publish] - client: '"+ client_id +"', channel: '"+ channel +"'");
|
||||
console.log("[publish] - data:");
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
bayeux.bind('connect', function(client_id) {
|
||||
console.log("[connect] - client: '"+ client_id +"'");
|
||||
});
|
||||
|
||||
|
||||
bayeux.bind('disconnect', function(client_id) {
|
||||
console.log("[disconnect] - client: '"+ client_id +"'");
|
||||
});
|
||||
|
||||
bayeux.addExtension(serverLog);
|
||||
bayeux.attach(server);
|
||||
|
||||
|
||||
bayeux.getClient().subscribe('/cool', function(message){
|
||||
console.log("OMG NEW MESSAGE CAME ***********");
|
||||
console.log(JSON.stringify(message));
|
||||
});
|
||||
|
||||
server.listen(5222);
|
||||
console.log("Started Faye Server");
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,723 +0,0 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Websocket.swift
|
||||
//
|
||||
// Created by Dalton Cherry on 7/16/14.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import Foundation
|
||||
import CoreFoundation
|
||||
|
||||
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: NSData)
|
||||
}
|
||||
|
||||
public class WebSocket : NSObject, NSStreamDelegate {
|
||||
|
||||
enum OpCode : UInt8 {
|
||||
case ContinueFrame = 0x0
|
||||
case TextFrame = 0x1
|
||||
case BinaryFrame = 0x2
|
||||
//3-7 are reserved.
|
||||
case ConnectionClose = 0x8
|
||||
case Ping = 0x9
|
||||
case Pong = 0xA
|
||||
//B-F reserved.
|
||||
}
|
||||
|
||||
enum CloseCode : UInt16 {
|
||||
case Normal = 1000
|
||||
case GoingAway = 1001
|
||||
case ProtocolError = 1002
|
||||
case ProtocolUnhandledType = 1003
|
||||
// 1004 reserved.
|
||||
case NoStatusReceived = 1005
|
||||
//1006 reserved.
|
||||
case Encoding = 1007
|
||||
case PolicyViolated = 1008
|
||||
case MessageTooBig = 1009
|
||||
}
|
||||
|
||||
enum InternalErrorCode : UInt16 {
|
||||
// 0-999 WebSocket status codes not used
|
||||
case OutputStreamWriteError = 1
|
||||
}
|
||||
|
||||
//Where the callback is executed. It defaults to the main UI thread queue.
|
||||
public var queue = dispatch_get_main_queue()
|
||||
|
||||
var optionalProtocols : Array<String>?
|
||||
//Constant Values.
|
||||
let headerWSUpgradeName = "Upgrade"
|
||||
let headerWSUpgradeValue = "websocket"
|
||||
let headerWSHostName = "Host"
|
||||
let headerWSConnectionName = "Connection"
|
||||
let headerWSConnectionValue = "Upgrade"
|
||||
let headerWSProtocolName = "Sec-WebSocket-Protocol"
|
||||
let headerWSVersionName = "Sec-WebSocket-Version"
|
||||
let headerWSVersionValue = "13"
|
||||
let headerWSKeyName = "Sec-WebSocket-Key"
|
||||
let headerOriginName = "Origin"
|
||||
let headerWSAcceptName = "Sec-WebSocket-Accept"
|
||||
let BUFFER_MAX = 2048
|
||||
let FinMask: UInt8 = 0x80
|
||||
let OpCodeMask: UInt8 = 0x0F
|
||||
let RSVMask: UInt8 = 0x70
|
||||
let MaskMask: UInt8 = 0x80
|
||||
let PayloadLenMask: UInt8 = 0x7F
|
||||
let MaxFrameSize: Int = 32
|
||||
|
||||
class WSResponse {
|
||||
var isFin = false
|
||||
var code: OpCode = .ContinueFrame
|
||||
var bytesLeft = 0
|
||||
var frameCount = 0
|
||||
var buffer: NSMutableData?
|
||||
}
|
||||
|
||||
public weak var delegate: WebSocketDelegate?
|
||||
private var url: NSURL
|
||||
private var inputStream: NSInputStream?
|
||||
private var outputStream: NSOutputStream?
|
||||
private var isRunLoop = false
|
||||
private var connected = false
|
||||
private var isCreated = false
|
||||
private var writeQueue: NSOperationQueue?
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
//init the websocket with a url
|
||||
public init(url: NSURL) {
|
||||
self.url = url
|
||||
}
|
||||
//used for setting protocols.
|
||||
public convenience init(url: NSURL, protocols: Array<String>) {
|
||||
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(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), {
|
||||
self.isCreated = true
|
||||
self.createHTTPRequest()
|
||||
self.isCreated = false
|
||||
})
|
||||
}
|
||||
|
||||
///disconnect from the websocket server
|
||||
public func disconnect() {
|
||||
writeError(CloseCode.Normal.rawValue)
|
||||
}
|
||||
|
||||
///write a string to the websocket. This sends it as a text frame.
|
||||
public func writeString(str: String) {
|
||||
dequeueWrite(str.dataUsingEncoding(NSUTF8StringEncoding)!, code: .TextFrame)
|
||||
}
|
||||
|
||||
///write binary data to the websocket. This sends it as a binary frame.
|
||||
public func writeData(data: NSData) {
|
||||
dequeueWrite(data, code: .BinaryFrame)
|
||||
}
|
||||
|
||||
//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) {
|
||||
dequeueWrite(data, code: .Ping)
|
||||
}
|
||||
//private methods below!
|
||||
|
||||
//private method that starts the connection
|
||||
private func createHTTPRequest() {
|
||||
|
||||
let str: NSString = url.absoluteString!
|
||||
let urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, "GET",
|
||||
url, kCFHTTPVersion1_1)
|
||||
|
||||
var port = url.port
|
||||
if port == nil {
|
||||
if url.scheme == "wss" || url.scheme == "https" {
|
||||
port = 443
|
||||
} else {
|
||||
port = 80
|
||||
}
|
||||
}
|
||||
self.addHeader(urlRequest, key: headerWSUpgradeName, val: headerWSUpgradeValue)
|
||||
self.addHeader(urlRequest, key: headerWSConnectionName, val: headerWSConnectionValue)
|
||||
if let protocols = optionalProtocols {
|
||||
self.addHeader(urlRequest, key: headerWSProtocolName, val: ",".join(protocols))
|
||||
}
|
||||
self.addHeader(urlRequest, key: headerWSVersionName, val: headerWSVersionValue)
|
||||
self.addHeader(urlRequest, key: headerWSKeyName, val: self.generateWebSocketKey())
|
||||
self.addHeader(urlRequest, key: headerOriginName, val: url.absoluteString!)
|
||||
self.addHeader(urlRequest, key: headerWSHostName, val: "\(url.host!):\(port!)")
|
||||
for (key,value) in headers {
|
||||
self.addHeader(urlRequest, key: key, val: value)
|
||||
}
|
||||
|
||||
let serializedRequest: NSData = CFHTTPMessageCopySerializedMessage(urlRequest.takeUnretainedValue()).takeUnretainedValue()
|
||||
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) {
|
||||
let nsKey: NSString = key
|
||||
let nsVal: NSString = val
|
||||
CFHTTPMessageSetHeaderFieldValue(urlRequest.takeUnretainedValue(),
|
||||
nsKey,
|
||||
nsVal)
|
||||
}
|
||||
//generate a websocket key as needed in rfc
|
||||
private func generateWebSocketKey() -> String {
|
||||
var key = ""
|
||||
let seed = 16
|
||||
for (var i = 0; i < seed; i++) {
|
||||
let uni = UnicodeScalar(UInt32(97 + arc4random_uniform(25)))
|
||||
key += "\(Character(uni))"
|
||||
}
|
||||
var data = key.dataUsingEncoding(NSUTF8StringEncoding)
|
||||
var baseKey = data?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(0))
|
||||
return baseKey!
|
||||
}
|
||||
//Start the stream connection and write the data to the output stream
|
||||
private func initStreamsWithData(data: NSData, _ 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)
|
||||
|
||||
var readStream: Unmanaged<CFReadStream>?
|
||||
var writeStream: Unmanaged<CFWriteStream>?
|
||||
let h: NSString = url.host!
|
||||
CFStreamCreatePairWithSocketToHost(nil, h, UInt32(port), &readStream, &writeStream)
|
||||
inputStream = readStream!.takeUnretainedValue()
|
||||
outputStream = writeStream!.takeUnretainedValue()
|
||||
|
||||
inputStream!.delegate = self
|
||||
outputStream!.delegate = self
|
||||
if url.scheme == "wss" || url.scheme == "https" {
|
||||
inputStream!.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey)
|
||||
outputStream!.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey)
|
||||
}
|
||||
if self.voipEnabled {
|
||||
inputStream!.setProperty(NSStreamNetworkServiceTypeVoIP, forKey: NSStreamNetworkServiceType)
|
||||
outputStream!.setProperty(NSStreamNetworkServiceTypeVoIP, forKey: NSStreamNetworkServiceType)
|
||||
}
|
||||
if self.selfSignedSSL {
|
||||
let settings: Dictionary<NSObject, NSObject> = [kCFStreamSSLValidatesCertificateChain: NSNumber(bool:false), kCFStreamSSLPeerName: kCFNull]
|
||||
inputStream!.setProperty(settings, forKey: kCFStreamPropertySSLSettings as! String)
|
||||
outputStream!.setProperty(settings, forKey: kCFStreamPropertySSLSettings as! String)
|
||||
}
|
||||
isRunLoop = true
|
||||
inputStream!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
|
||||
outputStream!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
|
||||
inputStream!.open()
|
||||
outputStream!.open()
|
||||
let bytes = UnsafePointer<UInt8>(data.bytes)
|
||||
outputStream!.write(bytes, maxLength: data.length)
|
||||
while(isRunLoop) {
|
||||
NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate.distantFuture() as! NSDate)
|
||||
}
|
||||
}
|
||||
//delegate for the stream methods. Processes incoming bytes
|
||||
public func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) {
|
||||
|
||||
if eventCode == .HasBytesAvailable {
|
||||
if(aStream == inputStream) {
|
||||
processInputStream()
|
||||
}
|
||||
} else if eventCode == .ErrorOccurred {
|
||||
disconnectStream(aStream.streamError)
|
||||
} else if eventCode == .EndEncountered {
|
||||
disconnectStream(nil)
|
||||
}
|
||||
}
|
||||
//disconnect the stream object
|
||||
private func disconnectStream(error: NSError?) {
|
||||
if writeQueue != nil {
|
||||
writeQueue!.waitUntilAllOperationsAreFinished()
|
||||
}
|
||||
if let stream = inputStream {
|
||||
stream.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
|
||||
stream.close()
|
||||
}
|
||||
if let stream = outputStream {
|
||||
stream.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
|
||||
stream.close()
|
||||
}
|
||||
outputStream = nil
|
||||
isRunLoop = false
|
||||
connected = false
|
||||
dispatch_async(queue,{
|
||||
if let disconnectBlock = self.disconnectedBlock {
|
||||
disconnectBlock(error)
|
||||
}
|
||||
self.delegate?.websocketDidDisconnect(self, error: error)
|
||||
})
|
||||
}
|
||||
|
||||
///handles the incoming bytes and sending them to the proper processing method
|
||||
private func processInputStream() {
|
||||
let buf = NSMutableData(capacity: BUFFER_MAX)
|
||||
var buffer = UnsafeMutablePointer<UInt8>(buf!.bytes)
|
||||
let length = inputStream!.read(buffer, maxLength: BUFFER_MAX)
|
||||
if length > 0 {
|
||||
if !connected {
|
||||
connected = processHTTP(buffer, bufferLen: length)
|
||||
if !connected {
|
||||
dispatch_async(queue,{
|
||||
//self.workaroundMethod()
|
||||
let error = self.errorWithDetail("Invalid HTTP upgrade", code: 1)
|
||||
if let disconnect = self.disconnectedBlock {
|
||||
disconnect(error)
|
||||
}
|
||||
self.delegate?.websocketDidDisconnect(self, error: error)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
var process = false
|
||||
if inputQueue.count == 0 {
|
||||
process = true
|
||||
}
|
||||
inputQueue.append(NSData(bytes: buffer, length: length))
|
||||
if process {
|
||||
dequeueInput()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
///dequeue the incoming input so it is processed in order
|
||||
private func dequeueInput() {
|
||||
if inputQueue.count > 0 {
|
||||
let data = inputQueue[0]
|
||||
var work = data
|
||||
if (fragBuffer != nil) {
|
||||
var combine = NSMutableData(data: fragBuffer!)
|
||||
combine.appendData(data)
|
||||
work = combine
|
||||
fragBuffer = nil
|
||||
}
|
||||
let buffer = UnsafePointer<UInt8>(work.bytes)
|
||||
processRawMessage(buffer, bufferLen: work.length)
|
||||
inputQueue = inputQueue.filter{$0 != data}
|
||||
dequeueInput()
|
||||
}
|
||||
}
|
||||
///Finds the HTTP Packet in the TCP stream, by looking for the CRLF.
|
||||
private func processHTTP(buffer: UnsafePointer<UInt8>, bufferLen: Int) -> Bool {
|
||||
let CRLFBytes = [UInt8(ascii: "\r"), UInt8(ascii: "\n"), UInt8(ascii: "\r"), UInt8(ascii: "\n")]
|
||||
var k = 0
|
||||
var totalSize = 0
|
||||
for var i = 0; i < bufferLen; i++ {
|
||||
if buffer[i] == CRLFBytes[k] {
|
||||
k++
|
||||
if k == 3 {
|
||||
totalSize = i + 1
|
||||
break
|
||||
}
|
||||
} else {
|
||||
k = 0
|
||||
}
|
||||
}
|
||||
if totalSize > 0 {
|
||||
if validateResponse(buffer, bufferLen: totalSize) {
|
||||
dispatch_async(queue,{
|
||||
//self.workaroundMethod()
|
||||
if let connectBlock = self.connectedBlock {
|
||||
connectBlock()
|
||||
}
|
||||
self.delegate?.websocketDidConnect(self)
|
||||
})
|
||||
totalSize += 1 //skip the last \n
|
||||
let restSize = bufferLen - totalSize
|
||||
if restSize > 0 {
|
||||
processRawMessage((buffer+totalSize),bufferLen: restSize)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
///validates the HTTP is a 101 as per the RFC spec
|
||||
private func validateResponse(buffer: UnsafePointer<UInt8>, bufferLen: Int) -> Bool {
|
||||
let response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, 0)
|
||||
CFHTTPMessageAppendBytes(response.takeUnretainedValue(), buffer, bufferLen)
|
||||
if CFHTTPMessageGetResponseStatusCode(response.takeUnretainedValue()) != 101 {
|
||||
return false
|
||||
}
|
||||
let cfHeaders = CFHTTPMessageCopyAllHeaderFields(response.takeUnretainedValue())
|
||||
let headers: NSDictionary = cfHeaders.takeUnretainedValue()
|
||||
let acceptKey = headers[headerWSAcceptName] as! NSString
|
||||
if acceptKey.length > 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
///process the websocket data
|
||||
private func processRawMessage(buffer: UnsafePointer<UInt8>, bufferLen: Int) {
|
||||
var response = readStack.last
|
||||
if response != nil && bufferLen < 2 {
|
||||
fragBuffer = NSData(bytes: buffer, length: bufferLen)
|
||||
return
|
||||
}
|
||||
if response != nil && response!.bytesLeft > 0 {
|
||||
let resp = response!
|
||||
var len = resp.bytesLeft
|
||||
var extra = bufferLen - resp.bytesLeft
|
||||
if resp.bytesLeft > bufferLen {
|
||||
len = bufferLen
|
||||
extra = 0
|
||||
}
|
||||
resp.bytesLeft -= len
|
||||
resp.buffer?.appendData(NSData(bytes: buffer, length: len))
|
||||
processResponse(resp)
|
||||
var offset = bufferLen - extra
|
||||
if extra > 0 {
|
||||
processExtra((buffer+offset), bufferLen: extra)
|
||||
}
|
||||
return
|
||||
} else {
|
||||
let isFin = (FinMask & buffer[0])
|
||||
let receivedOpcode = (OpCodeMask & buffer[0])
|
||||
let isMasked = (MaskMask & buffer[1])
|
||||
let payloadLen = (PayloadLenMask & buffer[1])
|
||||
var offset = 2
|
||||
if((isMasked > 0 || (RSVMask & buffer[0]) > 0) && receivedOpcode != OpCode.Pong.rawValue) {
|
||||
let errCode = CloseCode.ProtocolError.rawValue
|
||||
let error = self.errorWithDetail("masked and rsv data is not currently supported", code: errCode)
|
||||
if let disconnect = self.disconnectedBlock {
|
||||
disconnect(error)
|
||||
}
|
||||
self.delegate?.websocketDidDisconnect(self, error: error)
|
||||
writeError(errCode)
|
||||
return
|
||||
}
|
||||
let isControlFrame = (receivedOpcode == OpCode.ConnectionClose.rawValue || receivedOpcode == OpCode.Ping.rawValue)
|
||||
if !isControlFrame && (receivedOpcode != OpCode.BinaryFrame.rawValue && receivedOpcode != OpCode.ContinueFrame.rawValue &&
|
||||
receivedOpcode != OpCode.TextFrame.rawValue && receivedOpcode != OpCode.Pong.rawValue) {
|
||||
let errCode = CloseCode.ProtocolError.rawValue
|
||||
let error = self.errorWithDetail("unknown opcode: \(receivedOpcode)", code: errCode)
|
||||
if let disconnect = self.disconnectedBlock {
|
||||
disconnect(error)
|
||||
}
|
||||
self.delegate?.websocketDidDisconnect(self, error: error)
|
||||
writeError(errCode)
|
||||
return
|
||||
}
|
||||
if isControlFrame && isFin == 0 {
|
||||
let errCode = CloseCode.ProtocolError.rawValue
|
||||
let error = self.errorWithDetail("control frames can't be fragmented", code: errCode)
|
||||
if let disconnect = self.disconnectedBlock {
|
||||
disconnect(error)
|
||||
}
|
||||
self.delegate?.websocketDidDisconnect(self, error: error)
|
||||
writeError(errCode)
|
||||
return
|
||||
}
|
||||
if receivedOpcode == OpCode.ConnectionClose.rawValue {
|
||||
var code = CloseCode.Normal.rawValue
|
||||
if payloadLen == 1 {
|
||||
code = CloseCode.ProtocolError.rawValue
|
||||
} else if payloadLen > 1 {
|
||||
var codeBuffer = UnsafePointer<UInt16>((buffer+offset))
|
||||
code = codeBuffer[0].bigEndian
|
||||
if code < 1000 || (code > 1003 && code < 1007) || (code > 1011 && code < 3000) {
|
||||
code = CloseCode.ProtocolError.rawValue
|
||||
}
|
||||
offset += 2
|
||||
}
|
||||
if payloadLen > 2 {
|
||||
let len = Int(payloadLen-2)
|
||||
if len > 0 {
|
||||
let bytes = UnsafePointer<UInt8>((buffer+offset))
|
||||
var str: NSString? = NSString(data: NSData(bytes: bytes, length: len), encoding: NSUTF8StringEncoding)
|
||||
if str == nil {
|
||||
code = CloseCode.ProtocolError.rawValue
|
||||
}
|
||||
}
|
||||
}
|
||||
let error = self.errorWithDetail("connection closed by server", code: code)
|
||||
if let disconnect = self.disconnectedBlock {
|
||||
disconnect(error)
|
||||
}
|
||||
self.delegate?.websocketDidDisconnect(self, error: error)
|
||||
writeError(code)
|
||||
return
|
||||
}
|
||||
if isControlFrame && payloadLen > 125 {
|
||||
writeError(CloseCode.ProtocolError.rawValue)
|
||||
return
|
||||
}
|
||||
var dataLength = UInt64(payloadLen)
|
||||
if dataLength == 127 {
|
||||
let bytes = UnsafePointer<UInt64>((buffer+offset))
|
||||
dataLength = bytes[0].bigEndian
|
||||
offset += sizeof(UInt64)
|
||||
} else if dataLength == 126 {
|
||||
let bytes = UnsafePointer<UInt16>((buffer+offset))
|
||||
dataLength = UInt64(bytes[0].bigEndian)
|
||||
offset += sizeof(UInt16)
|
||||
}
|
||||
var len = dataLength
|
||||
if dataLength > UInt64(bufferLen) {
|
||||
len = UInt64(bufferLen-offset)
|
||||
}
|
||||
var data: NSData!
|
||||
if len < 0 {
|
||||
len = 0
|
||||
data = NSData()
|
||||
} else {
|
||||
data = NSData(bytes: UnsafePointer<UInt8>((buffer+offset)), length: Int(len))
|
||||
}
|
||||
if receivedOpcode == OpCode.Pong.rawValue {
|
||||
let step = Int(offset+numericCast(len))
|
||||
let extra = bufferLen-step
|
||||
if extra > 0 {
|
||||
processRawMessage((buffer+step), bufferLen: extra)
|
||||
}
|
||||
return
|
||||
}
|
||||
var response = readStack.last
|
||||
if isControlFrame {
|
||||
response = nil //don't append pings
|
||||
}
|
||||
if isFin == 0 && receivedOpcode == OpCode.ContinueFrame.rawValue && response == nil {
|
||||
let errCode = CloseCode.ProtocolError.rawValue
|
||||
let error = self.errorWithDetail("continue frame before a binary or text frame", code: errCode)
|
||||
if let disconnect = self.disconnectedBlock {
|
||||
disconnect(error)
|
||||
}
|
||||
self.delegate?.websocketDidDisconnect(self, error: error)
|
||||
writeError(errCode)
|
||||
return
|
||||
}
|
||||
var isNew = false
|
||||
if(response == nil) {
|
||||
if receivedOpcode == OpCode.ContinueFrame.rawValue {
|
||||
let errCode = CloseCode.ProtocolError.rawValue
|
||||
let error = self.errorWithDetail("first frame can't be a continue frame",
|
||||
code: errCode)
|
||||
if let disconnect = self.disconnectedBlock {
|
||||
disconnect(error)
|
||||
}
|
||||
self.delegate?.websocketDidDisconnect(self, error: error)
|
||||
writeError(errCode)
|
||||
return
|
||||
}
|
||||
isNew = true
|
||||
response = WSResponse()
|
||||
response!.code = OpCode(rawValue: receivedOpcode)!
|
||||
response!.bytesLeft = Int(dataLength)
|
||||
response!.buffer = NSMutableData(data: data)
|
||||
} else {
|
||||
if receivedOpcode == OpCode.ContinueFrame.rawValue {
|
||||
response!.bytesLeft = Int(dataLength)
|
||||
} else {
|
||||
let errCode = CloseCode.ProtocolError.rawValue
|
||||
let error = self.errorWithDetail("second and beyond of fragment message must be a continue frame",
|
||||
code: errCode)
|
||||
if let disconnect = self.disconnectedBlock {
|
||||
disconnect(error)
|
||||
}
|
||||
self.delegate?.websocketDidDisconnect(self, error: error)
|
||||
writeError(errCode)
|
||||
return
|
||||
}
|
||||
response!.buffer!.appendData(data)
|
||||
}
|
||||
if response != nil {
|
||||
response!.bytesLeft -= Int(len)
|
||||
response!.frameCount++
|
||||
response!.isFin = isFin > 0 ? true : false
|
||||
if(isNew) {
|
||||
readStack.append(response!)
|
||||
}
|
||||
processResponse(response!)
|
||||
}
|
||||
|
||||
let step = Int(offset+numericCast(len))
|
||||
let extra = bufferLen-step
|
||||
if(extra > 0) {
|
||||
processExtra((buffer+step), bufferLen: extra)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
///process the extra of a buffer
|
||||
private func processExtra(buffer: UnsafePointer<UInt8>, bufferLen: Int) {
|
||||
if bufferLen < 2 {
|
||||
fragBuffer = NSData(bytes: buffer, length: bufferLen)
|
||||
} else {
|
||||
processRawMessage(buffer, bufferLen: bufferLen)
|
||||
}
|
||||
}
|
||||
|
||||
///process the finished response of a buffer
|
||||
private func processResponse(response: WSResponse) -> Bool {
|
||||
if response.isFin && response.bytesLeft <= 0 {
|
||||
if response.code == .Ping {
|
||||
let data = response.buffer! //local copy so it is perverse for writing
|
||||
dequeueWrite(data, code: OpCode.Pong)
|
||||
} else if response.code == .TextFrame {
|
||||
var str: NSString? = NSString(data: response.buffer!, encoding: NSUTF8StringEncoding)
|
||||
if str == nil {
|
||||
writeError(CloseCode.Encoding.rawValue)
|
||||
return false
|
||||
}
|
||||
dispatch_async(queue,{
|
||||
if let textBlock = self.receivedTextBlock{
|
||||
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,{
|
||||
//self.workaroundMethod()
|
||||
if let dataBlock = self.receivedDataBlock{
|
||||
dataBlock(data)
|
||||
}
|
||||
self.delegate?.websocketDidReceiveData(self, data: data)
|
||||
})
|
||||
}
|
||||
readStack.removeLast()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
///Create an error
|
||||
private func errorWithDetail(detail: String, code: UInt16) -> NSError {
|
||||
var details = Dictionary<String,String>()
|
||||
details[NSLocalizedDescriptionKey] = detail
|
||||
return NSError(domain: "Websocket", code: Int(code), userInfo: details)
|
||||
}
|
||||
|
||||
///write a an error to the socket
|
||||
private func writeError(code: UInt16) {
|
||||
let buf = NSMutableData(capacity: sizeof(UInt16))
|
||||
var buffer = UnsafeMutablePointer<UInt16>(buf!.bytes)
|
||||
buffer[0] = code.bigEndian
|
||||
dequeueWrite(NSData(bytes: buffer, length: sizeof(UInt16)), code: .ConnectionClose)
|
||||
}
|
||||
///used to write things to the stream in a
|
||||
private func dequeueWrite(data: NSData, code: OpCode) {
|
||||
if writeQueue == nil {
|
||||
writeQueue = NSOperationQueue()
|
||||
writeQueue!.maxConcurrentOperationCount = 1
|
||||
}
|
||||
writeQueue!.addOperationWithBlock {
|
||||
//stream isn't ready, let's wait
|
||||
var tries = 0;
|
||||
while self.outputStream == nil || !self.connected {
|
||||
if(tries < 5) {
|
||||
sleep(1);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
tries++;
|
||||
}
|
||||
if !self.connected {
|
||||
return
|
||||
}
|
||||
var offset = 2
|
||||
UINT16_MAX
|
||||
let bytes = UnsafeMutablePointer<UInt8>(data.bytes)
|
||||
let dataLength = data.length
|
||||
let frame = NSMutableData(capacity: dataLength + self.MaxFrameSize)
|
||||
let buffer = UnsafeMutablePointer<UInt8>(frame!.mutableBytes)
|
||||
buffer[0] = self.FinMask | code.rawValue
|
||||
if dataLength < 126 {
|
||||
buffer[1] = CUnsignedChar(dataLength)
|
||||
} else if dataLength <= Int(UInt16.max) {
|
||||
buffer[1] = 126
|
||||
var sizeBuffer = UnsafeMutablePointer<UInt16>((buffer+offset))
|
||||
sizeBuffer[0] = UInt16(dataLength).bigEndian
|
||||
offset += sizeof(UInt16)
|
||||
} else {
|
||||
buffer[1] = 127
|
||||
var sizeBuffer = UnsafeMutablePointer<UInt64>((buffer+offset))
|
||||
sizeBuffer[0] = UInt64(dataLength).bigEndian
|
||||
offset += sizeof(UInt64)
|
||||
}
|
||||
buffer[1] |= self.MaskMask
|
||||
var maskKey = UnsafeMutablePointer<UInt8>(buffer + offset)
|
||||
SecRandomCopyBytes(kSecRandomDefault, Int(sizeof(UInt32)), maskKey)
|
||||
offset += sizeof(UInt32)
|
||||
|
||||
for (var i = 0; i < dataLength; i++) {
|
||||
buffer[offset] = bytes[i] ^ maskKey[i % sizeof(UInt32)]
|
||||
offset += 1
|
||||
}
|
||||
var total = 0
|
||||
while true {
|
||||
if self.outputStream == nil {
|
||||
break
|
||||
}
|
||||
let writeBuffer = UnsafePointer<UInt8>(frame!.bytes+total)
|
||||
var len = self.outputStream?.write(writeBuffer, maxLength: offset-total)
|
||||
if len == nil || len! < 0 {
|
||||
var error: NSError?
|
||||
if let streamError = self.outputStream?.streamError {
|
||||
error = streamError
|
||||
} else {
|
||||
let errCode = InternalErrorCode.OutputStreamWriteError.rawValue
|
||||
error = self.errorWithDetail("output stream error during write", code: errCode)
|
||||
}
|
||||
if let disconnect = self.disconnectedBlock {
|
||||
disconnect(error)
|
||||
}
|
||||
self.delegate?.websocketDidDisconnect(self, error: error)
|
||||
break
|
||||
} else {
|
||||
total += len!
|
||||
}
|
||||
if total >= offset {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"name" : "fayeswift-demo",
|
||||
"version" : "1.0.0",
|
||||
"dependencies" : {
|
||||
"faye" : "1.0.3"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 19 KiB |
Reference in New Issue
Block a user