WebSockets on iOS with Starscream

Learn how to use Starscream to add always-on networking with WebSockets in this iOS tutorial. By Aaron Douglas.

Leave a rating/review
Save for later
Share

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!