WebSock in iOS with URLSessionWebSocketTask & Starscream

Photo by Karl Anderson on Unsplash
Photo by Karl Anderson on Unsplash
This article will introduce how to use URLSessionWebSocketTask and the Starsream to connect to a WebSocket server.

WebSocket has become more and more popular in recent years, and Apple also announced iOS 13 with WebSocket functionality at WWDC 2019. This article will introduce how to use URLSessionWebSocketTask and the Starsream to connect to a WebSocket server.

The complete code can be found in .

URLSessionWebSocketTask

URLSessionWebSocketTask is available on iOS 13, so make sure that Xcode’s iOS Deployment Target is set after 13.

Opening Connections

Declare a request with URLRequest(url:), and call URLSession.webSocketTask(with:) to create a task. It returns URLSessionWebSocketTask, start the task by calling .resume() to open a connection. It’s simple, isn’t it!

guard let url = URL(string: "wss://echo.websocket.org/") else {
    print("Error: can not create URL")
    return
}

let request = URLRequest(url: url)

let webSocketTask = URLSession.shared.webSocketTask(with: request)
webSocketTask.resume()

Sending Messages

Set a message to be sent into URLSessionWebSocketTask.Message, and call URLSessionWebSocketTask.send()to send the message.

let message = URLSessionWebSocketTask.Message.string("Hello WebSocket")
webSocketTask.send(message) { error in
    if let error = error {
	print(error)
    }
}

Receiving Messages

URLSessionWebSocketTask.receive() is used to receive messages. it process one message at a time. Received messages are type of URLSessionWebSocketTask.Message.

It should be noted that when you call .receive(), it will only receive one message once. So after the handler has processed one message, remember to call .receive() again before leaving.

private func receive() {
    webSocketTask.receive { result in
        switch result {
        case .success(let message):
            switch message {
            case .string(let text):
                print("Received string: \(text)")
            case .data(let data):
                print("Received data: \(data)")
            @unknown default:
                fatalError()
            }

        case .failure(let error):
            print(error)
        }

        self.receive()
    }
}

Disconnecting

Call URLSessionWebSocketTask.cancel() to disconnect the connection.

webSocketTask.cancel(with: .goingAway, reason: nil)

Monitoring Connection Statuses

If you want to monitor a connection status, you need to implement URLSessionWebSocketDelegate. It monitors two events, one is triggered when the connection is opened, and the other is triggered when the connection is disconnected.

extension URLSessionWebSocket: URLSessionWebSocketDelegate {
    public func urlSession(_ session: URLSession,
                           webSocketTask: URLSessionWebSocketTask,
                           didOpenWithProtocol protocol: String?) {
        print("URLSessionWebSocketTask is connected")
    }

    public func urlSession(_ session: URLSession,
                           webSocketTask: URLSessionWebSocketTask,
                           didCloseWith closeCode: URLSessionWebSocketTask.CloseCode,
                           reason: Data?) {
        let reasonString: String
        if let reason = reason, let string = String(data: reason, encoding: .utf8) {
            reasonString = string
        } else {
            reasonString = ""
        }

        print("URLSessionWebSocketTask is closed: code=\(closeCode), reason=\(reasonString)")
    }
}

URLSessionWebSocketDelegate has to be set to URLSession at the beginning . Therefore, you need to declare a URLSession yourself when opening a connection, but not use URLSession.defualt.

let urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: .main)
let request = URLRequest(url: url)

webSocketTask = urlSession.webSocketTask(with: request)
webSocketTask.resume()

Starsream

URLSessionWebSocketTask looks good to use, but it has some problems. First, it is only available after iOS 13. iOS 13 is still new. We hope that iPhone users who have not upgraded can also install our App. The other is that URLSessionWebSocketTask is not very convenient in the way of receiving messages and monitoring events. Starsream can solve the above problems for us.

First, install Starsram framework. If you are using CocoaPods, put this line in your Podfile.

pod 'Starscream', '~> 4.0.0' 

If you are not familiar with CocoaPods operation, you can refer to this article.

Opening Connections

Declare a WebSocket with an URL, and call .connect() to open a connection.

import Starscream

guard let url = URL(string: "wss://echo.websocket.org/") else {
    print("Error: can not create URL")
    return
}

let request = URLRequest(url: url)

webSocket = WebSocket(request: request)
webSocket.connect()

Sending Messages

Call WebSocket.write(string:) to send a message. Compared with URLSessionWebSocketTask, it is really much simpler!

webSocket.write(string: message)

Receiving Messages and Monitoring Connection Statuses

Starscream handles message reception and connection status by using events. In particular for message reception, it’s much more intuitive than  URLSessionWebSocketTask.

webSocket.delegate = self

extension StarscreamWebSocket: WebSocketDelegate {
    func didReceive(event: WebSocketEvent, client: WebSocket) {
        switch event {
        case .connected(_):
            print("WebSocket is connected")
        case .disconnected(let reason, let code):
            print("Disconnected: code=\(code), reason=\(reason)")
        case .text(let message):
            print("Received: \(message)")
        case .binary(_):
            break
        case .pong(_):
            break
        case .ping(_):
            break
        case .error(let error):
            print(error ?? "")
        case .viabilityChanged(_):
            break
        case .reconnectSuggested(_):
            break
        case .cancelled:
            print("WebSocket is cancelled")
        }
    }
}

Disconnecting

Call WebSocket.disconnect() to disconnect a connection.

webSocket.disconnect()

Conclusion

We describe how to implement WebSocket through URLSessionWebSocketTask and Starscream. Comparing these two methods, we would recommend Starscream. While URLSessionWebSocketTask is built in iOS SDK, but its use and popularity are far less than Starscream.

Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like
Photo by Alex Alvarez on Unsplash
Read More

Dispatch Queue Tutorial

Grand Central Dispatch (GCD) provides efficient concurrent processing so that we don’t need to directly manage multiple threads. Its dispatch queues can execute tasks serially or concurrently.
Read More