WebSockets on iOS with Starscream

Aaron Douglas

Websockets-featureTraditional networking techniques (namely Berkeley sockets) are considered reliable and robust. However, Berkeley sockets don’t work well with web technologies like proxies and firewalls. WebSockets appeared in 2011 as a new way of establishing two-way communication channels between clients and servers. WebSockets are also more efficient when compared to multiple HTTP requests and allow for persistent connections.

Using WebSockets in iOS isn’t as straight-forward as it could be. The iOS and Mac library Starscream dramatically simplifies the setup and usage of WebSockets.

Note: This tutorial assumes you have some familiarity with CocoaPods. If you don’t, please check out our CocoaPods tutorial first.

In this tutorial, you’ll be finishing the networking in an app named Emoji Communicator for this tutorial. Emoji Communicator lets you broadcast your current state of mind to everyone connected to the service with a single emoji character.

The original developer for Emoji Communicator was going to use HTTP requests with polling to watch for new messages, but WebSockets are the better approach for this feature. You’ll be using Starscream to hook into the backend webserver.

Getting Started

The first thing you need to use WebSockets is a web server. For this tutorial, you’ll start up a web server on your local machine. This demo web server runs on Node.js and uses a small JavaScript file to power it.

Therefore, you need to have Node.js installed first. To check if you have Node.js already installed, open a Terminal window and type the following:

node --version

If you receive an error, follow the steps below to download and install Node.js. If instead you receive a version of Node.js in response, then you can skip the download section below.

Downloading and Installing Node.js

Download the current Node.js installer at https://nodejs.org/ (v6.6.0 Current as of 22-Sep-2016). Once you download the package file (node-v6.6.0.pkg, for example) double click it to install. Follow the prompts; the default options should suffice.

Once the install completes, verify Node.js works properly on the command line using Terminal:

node --version

If you don’t see 6.6.0 (or whatever version you installed), or you see an error, double check that the installer worked properly.

The Chat Server

The application you’ll be using is a chat server. Download the example iOS application and web server code here. Unzip the file into a folder on your desktop or a folder of your choice. In Terminal, change to that directory, then change to the subfolder nodeapp.

The application requires a third-party module that is installed with npm, the package manager Node.js uses.

While in the chat server’s folder, execute the following:

npm install websocket

Type the following to start the chat server:

node chat-server.js

You should see something similar to the following:

Tue Sep 13 2016 18:54:44 GMT-0500 (CDT) Server is listening on port 1337

Next open the file frontend.html in a current web browser like Safari or Chrome.

WebSockets Server

Enter a name and then send a test message. If you want to test a second client, try opening a different browser or tab using the same URL. Login with a different name and send a message; you’ll see that the message comes through almost immediately in the other browser.

whoa

This demonstrates the power of WebSockets. Each browser has a single persistent connection to the web server — there’s no polling. When a message arrives, it’s broadcast out to every connected user. Switch back to Terminal and you should see all of the chat activity:

$ node chat-server.js
Tue Sep 13 2016 18:54:44 GMT-0500 (CDT) Server is listening on port 1337
Tue Sep 13 2016 18:55:19 GMT-0500 (CDT) Connection from origin null.
Tue Sep 13 2016 18:55:19 GMT-0500 (CDT) Connection accepted.
Tue Sep 13 2016 18:55:34 GMT-0500 (CDT) User is known as: Aaron with green color.
Tue Sep 13 2016 18:55:37 GMT-0500 (CDT) Received Message from Aaron: Hello
Tue Sep 13 2016 18:58:49 GMT-0500 (CDT) Connection from origin null.
Tue Sep 13 2016 18:58:49 GMT-0500 (CDT) Connection accepted.
Tue Sep 13 2016 18:58:51 GMT-0500 (CDT) User is known as: James with red color.
Tue Sep 13 2016 18:58:55 GMT-0500 (CDT) Received Message from James: This is pretty slick!
Tue Sep 13 2016 18:59:03 GMT-0500 (CDT) Received Message from James: :]
Tue Sep 13 2016 18:59:27 GMT-0500 (CDT) Peer undefined disconnected.

WebSockets

HTTP was first created in 1991 and was designed to be a request-response mechanism for communication. Web browsers work well with this paradigm; the user requests a web page and the server responds with the content. Sometimes the user may need to be notified of new data without issuing a request — in other words, a server push.

HTTP doesn’t handle the push model very well. Web service implementations before WebSockets meant creating some sort of polling mechanism from the web browser side, which isn’t terribly efficient.

WebSockets help facilitate the server-side push mechanism. Newer web browsers all support WebSockets, which makes it super-simple to set up and use. Connections over WebSockets persist to open, and most networks have no difficulty handling WebSockets connections.

WebSockets are commonly used in scenarios with rapidly or often-changed data. Web notifications in Facebook, real-time chat in Slack, and streaming stock prices in a trading application are good use cases for WebSockets.

Using WebSockets in iOS is not simple; you have to do a lot of setup and there’s no built-in API to help you. This is where Starscream helps you out — all of the headache is taken away with a small and simple-to-use library.

Emoji Communicator

EmojiCommunicator

Open the EmojiTransmitter.xcodeproj file in Xcode. Build and run the application in a simulator. The application is simple; it asks for the user’s name, then presents a screen to send emoji and shows any received emoji.

The application doesn’t do any networking calls yet. You’ll use Starscream to perform all the networking calls over a WebSocket to your server.

There are a number of different ways to get Starscream into your project. CocoaPods and Carthage are the two most common dependency managers. You can use either, but for this tutorial CocoaPods will be the dependency manager of choice.

First, close the Xcode project file you just opened. Next, open a Terminal window and change directories to the project’s folder.

A Podfile has already been created for the project with the Starscream pod referenced. Install the Pods with the following:

pod repo update; pod install

Once CocoaPods finishes, open the EmojiTransmitter.xcworkspace file in Xcode 8. Build and run the application to make sure nothing is broken.

Open ViewController.swift and add the following below import UIKit:

import Starscream

Next, add the following property to the ViewController class below username:

var socket = WebSocket(url: URL(string: "ws://localhost:1337/")!, protocols: ["chat"])

This is the magic to create a WebSocket connection. Notice the format of the URL; the protocol is ws, not http or https. A protocol is specified (chat) but it really depends on the server implementation whether this protocol is used or ignored. In this demo, it’s pretty much ignored.

Next, add the following method below viewDidLoad():

deinit {
  socket.disconnect(forceTimeout: 0)
  socket.delegate = nil
}

This forces the WebSocket connection to disconnect when the view controller is deallocated.

All of the magic happens with delegates in Starscream. Starscream alternately supports using closures if you prefer that over delegates.

Still inside ViewController.swift, add the following extension below the fileprivate extension:

// MARK: - WebSocketDelegate
extension ViewController : WebSocketDelegate {
  public func websocketDidConnect(_ socket: Starscream.WebSocket) {

  }
  
  public func websocketDidDisconnect(_ socket: Starscream.WebSocket, error: NSError?) {

  }
  
  public func websocketDidReceiveMessage(_ socket: Starscream.WebSocket, text: String) {

  }
  
  public func websocketDidReceiveData(_ socket: Starscream.WebSocket, data: Data) {

  }
}

These four delegate methods are required to be implemented for your code to compile.

Next, add the following to viewDidLoad(), directly below super.viewDidLoad():

socket.delegate = self
socket.connect()

Build and run the application. Enter a name for the user and tap Next. Go back to the Node.js application console and you should see a connect notice.

Now that you are able to make a connection to the Node.js application, the next step is to send your messages to the server for distribution.

First, replace sendMessage(_:) with the following:

func sendMessage(_ message: String) {
  socket.write(string: message)
}

This will send any messages (or emojis, in this application’s case) to the Node.js server.

Next, add the following to websocketDidConnect(_:):

socket.write(string: username)

This sends the username entered from the first screen as soon as the socket connects. This specific server implementation uses the first received message to define the username.

Add the following to websocketDidDisconnect(_:error:):

performSegue(withIdentifier: "websocketDisconnected", sender: self)

This pops the user back to the username screen if the socket disconnects for whatever reason. You should consider a more robust error handling routine if you decide to use Starscream inside your own application.

Next, add the following to websocketDidReceiveMessage(_:text:):

// 1
guard let data = text.data(using: .utf16),
  let jsonData = try? JSONSerialization.jsonObject(with: data),
  let jsonDict = jsonData as? [String: Any],
  let messageType = jsonDict["type"] as? String else {
    return
}

// 2
if messageType == "message",
  let messageData = jsonDict["data"] as? [String: Any],
  let messageAuthor = messageData["author"] as? String,
  let messageText = messageData["text"] as? String {

  messageReceived(messageText, senderName: messageAuthor)
}

The text received is literally plain text — if the payload is JSON, it has to be converted into collection objects. Let’s go through this step by step:

  1. First you convert the String to NSData. Next you pass the NSData to the JSONSerialization object to parse the payload and (hopefully) return a valid object. Finally you check that several important keys are set within the object. If the data is not valid, the method will exit early using the guard statement.
  2. The messages are filtered by messageType and then sent through to the existing messageReceived(messageText:, senderName:) method.

Below is an example of the JSON-formatted string received from the Node.js server:

{
  "type": "message",
  "data": {
    "time": 1472513071731,
    "text": ":]",
    "author": "iPhone Simulator",
    "color": "orange"
  }
}

Build and run the application. Immediately after you send a message, the emoji is updated with your selected emoji and username. Switch back to the web console and your emoji message will be there as well.

That’s all that’s required to use WebSockets with Starscream!

Where to Go From Here?

You can download the final project code here.

Emoji Communicator is a rather simplistic example of what WebSockets can be used for. If you want to leverage Starscream in a pre-existing service, there are several steps you can take to learn more:

If you have any questions or comments on this tutorial, please join the discussion below!

Team

Each tutorial at www.raywenderlich.com is created by a team of dedicated developers so that it meets our high quality standards. The team members who worked on this tutorial are:

Aaron Douglas

Aaron was that kid taking apart the mechanical and electrical appliances at five years of age to see how they worked. He never grew out of that core interest - to know how things work. He took an early interest in computer programming, figuring out how to get past security to be able to play games on his dad's computer. He's still that feisty nerd, but at least now he gets paid to do it.

Aaron works for Automattic (WordPress.com, Akismet, SimpleNote) as a Mobile Maker primarily on WordPress for iOS.

Find Aaron on Twitter as @astralbodies or at his blog at https://aaron.blog.

Other Items of Interest

Save time.
Learn more with our video courses.

raywenderlich.com Weekly

Sign up to receive the latest tutorials from raywenderlich.com each week, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

PragmaConf 2016 Come check out Alt U

Our Books

Our Team

Video Team

... 20 total!

Swift Team

... 15 total!

iOS Team

... 42 total!

Android Team

... 16 total!

macOS Team

... 11 total!

Unity Team

... 11 total!

Articles Team

... 12 total!

Resident Authors Team

... 15 total!