Derek Kinsman – Teehan+Lax /blog We define and design custom experiences in the digital channel Tue, 13 Jan 2015 19:25:18 +0000 en-US hourly 1 https://wordpress.org/?v=4.8.1 How-To: Socket.IO & Swift /blog/how-to-socket-io-swift/ /blog/how-to-socket-io-swift/#respond Wed, 03 Sep 2014 16:17:34 +0000 /blog/?p=11713 Note: I am not an iOS developer. I’ve only recently dived into Swift. Brendan Lynch—our iOS dev—has provided a great deal of support in getting Socket.IO running on iOS. I’m going to assume that you’re semi familiar with iOS dev (and pseudo-code) and that I don’t need to show you the basics. The code in this post was tested against Xcode 6 Beta 5. Finally, I would like to give a shout out to Jon Mumm who wrote an article several years ago about using SocketRocket with Objective-C to talk to Socket.IO that we used as a jumping off point. To be honest it was the only Apple/Socket.IO info I could find at all.

We’re currently working on a project in Labs that requires real time communication between a variety of devices. This also needs to happen passively. The best tool for that—that I’m aware of—is Socket.IO.

Socket.IO is a real-time event based communication engine. It primarily uses WebSockets but can fallback to Flash sockets, JSONP polling and Long Polling. Typically both the client and the server need to be running Socket.IO (according to Wikipedia).

For the web this is fine. We can just run a browser client with Socket.IO and there’s no problem. The Socket.IO team haven’t released native versions of the library for Android or iOS. There is a great websockets library for Android called AndroidSync that also has full support for Socket.IO. On Apple’s platforms it’s a different story. There are maybe two or three libraries that support Socket.IO however they’re outdated, broken, and their repo’s haven’t been active in 2+ years. However, the fine folks at Square released an Objective-C library called SocketRocket which handles websockets. In this tutorial we’ll look at using that library in a Swift project and how to spoof the Socket.IO handshake protocol so that we can have a native iPhone app communicate with a Socket.IO server without running Socket.IO on the client.

End Result:

Gather Your Things

Setup

Create a new Swift iOS project (single view application).

To import SocketRocket into your project simply drag it into XCode and when prompted to create a bridging header select yes (this is the file you use to import Obj-C code into a Swift project). If the prompt doesn’t show up simply create a new file called yourProjectName-Bridging-Header.h by choosing File → New → File → iOS → Source → Header. In that file you’ll add the following line:

#import "SocketRocket/SRWebSocket.h"

Confirm or add the bridging header file path to Build Settings → Swift Compiler – Code Generation → Objective-C Bridging Header. The path should be relative to your project, such as yourProjectName/yourProjectName-Bridging-Header.h. Our final bit of project setup is to include the required dependencies for SocketRocket. Switch tabs to Build Phase and under Link Binary With Libraries add the following libraries to the list:

  • Foundation.framework
  • Security.framework
  • CFNetwork.framework
  • libicucore.dylib

Communicating with Socket.IO

Our client (iOS) won’t be running Socket.IO so we’ll have to spoof/hack the Socket.IO API so we can communicate with it.

We’re going to spoof Socket.IO’s private buildHandshake() function which looks like this:

Socket.prototype.buildHandshake = function(){
  return {
   headers: this.request.headers,
    time: (new Date) + ‘’,
    address: this.conn.remoteAddress,
    xdomain: !!this.request.headers.origin,
    secure: !!this.request.connection.encrypted,
    issued: +(new Date),
    url: this.request.url,
    query: url.parse(this.request.url, true).query || {}
  };
};

What happens is the client sends a GET request to <server>/socket.io/1?t=<unix time stamp>. The server responds with an ID which we use to connect to Socket.IO with SocketRocket. Everything after this is Swift flavoured Socket.IO.

Time To Write Some Code

In our view controller we need to extend our main class so it is also of the type SRWebSocketDelegate. Next we’ll add two methods. The first is our initial handshake and generates a token for us to use to connect to Socket.IO. The second method takes that token and opens the connection.

func initHandshake() {
    let time:NSTimeInterval = NSDate().timeIntervalSince1970 * 1000
    
    var endpoint = "http://\(server)/socket.io/1?t=\(time)"
    
    var handshakeTask:NSURLSessionTask = session!.dataTaskWithURL(NSURL.URLWithString(endpoint), completionHandler: { (data:NSData!, response:NSURLResponse!, error:NSError!) in
        if !error {
            let stringData:NSString = NSString(data: data, encoding: NSUTF8StringEncoding)
            let handshakeToken:NSString = stringData.componentsSeparatedByString(":")[0] as NSString
            println("HANDSHAKE \(handshakeToken)")
            
            self.socketConnect(handshakeToken)
        }
    })
    handshakeTask.resume()
}

func socketConnect(token:NSString) {
    socketio = SRWebSocket(URLRequest: NSURLRequest(URL: NSURL(string: "ws://\(server)/socket.io/1/websocket/\(token)")))
    socketio!.delegate = self
    socketio!.open()
}

In the above animation you can see six lines up from the bottom info: handshake authorized[…] is when the our iPhone first initiates a request to the server and the very last line is when it and the server open a WebSocket. The next method we’ll add handles all the incoming messages. This is basically a switchboard for all the socket.on(){} events from the server. This is also a required method for SocketRocket. Even if the method does nothing it still needs to be there. What we’ve done is used an if/else conditional to check for incoming socket.emit(){}’s which fires off the corresponding method.

func webSocket(webSocket: SRWebSocket!, didReceiveMessage message: AnyObject!) {
    // All incoming messages ( socket.on() ) are received in this function. Parsed with JSON
    println("MESSAGE: \(message)")
    
    var jsonError:NSError?
    let messageArray = (message as NSString).componentsSeparatedByString(":::")
    let data:NSData = messageArray[messageArray.endIndex - 1].dataUsingEncoding(NSUTF8StringEncoding)
    var json:AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &jsonError)
    
    if json != nil {
        let event: NSString = json!["name"] as NSString
        let args: NSDictionary = (json!["args"] as NSArray)[0] as NSDictionary
        
        if (event.isEqualToString("one")) {
            didReceiveEventOne(args)
        }
        else if (event.isEqualToString("two")) {
            didReceiveEventTwo(args)
        }
        else if (event.isEqualToString("three")) {
            didReceiveEventThree(args)
        }
    }
}

Our final piece of code that we need to include is the socket.emit(){} that our app will send back to the server.

func didReceiveEventOne(args: NSDictionary) {
    var dict = [
        "name": "event",
        "args": [["Method": "One"]]
    ]
    var jsonSendError:NSError?
    var jsonSend = NSJSONSerialization.dataWithJSONObject(dict, options: NSJSONWritingOptions(0), error: &jsonSendError)
    var jsonString = NSString(data: jsonSend, encoding: NSUTF8StringEncoding)
    println("JSON SENT \(jsonString)")
    let str:NSString = "5:::\(jsonString)"
    socketio?.send(str)
}

The important bits of code are what is inside this method. In the demo code you can download that emit() is being triggered by a button press instead of happening as the result of an on() event. That said, let’s step through what is going on. In our webSocket() method we’re monitoring for three events from the server and when it sees them to fire a specific function:

Server:
socket.emit(‘one’,{/* stuff */};
socket.emit(‘two’,{/* stuff */};
socket.emit(‘three’,{/* stuff */};

Client:
socket.on(‘one’,{doEventOne()}; 
socket.on(‘two’,{doEventTwo()}; 
socket.on(‘three’,{doEventThree()};

That function in the above Gist that the client fires is a socket.emit() event:
socket.emit(‘event’,{Method: One}); 
socket.emit(‘event’,{Method: Two}); 
socket.emit(‘event’,{Method: Three});

In this animation you can see in the terminal the result from the button press which fires off a socket.emit(‘’,{});. You’re now on your way to using Socket.IO inside a native iOS app and using Apple’s new programming language. We’ve now got our app sending messages to our Socket.IO server.

Wrapping Up

We now have the ability to do real time event based communications between all the platforms we use in our day to day lives and can leverage low level hardware functionality of a mobile device that is usually unavailable to the mobile web to trigger these Socket.IO events. This could be quite powerful for location based games such as Google and Niantic Labs’s alternate reality MMO Ingress.

As we move to closer to the Internet of Everything we’ll need to start considering which devices and sensors require event driven real-time feedback and messaging. Weather and emergency services could use such systems inside weather stations to provide messaging based on specific events, wind speeds, rainfall levels, et. al.

Socket.IO recently released their stable 1.0 release several weeks ago, is free and open source, and is well supported by both the core developers and the community. Real-time communication has never been easier.

What will you make?

Grab the full project on Github.

]]>
/blog/how-to-socket-io-swift/feed/ 0