Introduction: Why WebSocket is Important for a Swift Developer
In modern mobile and desktop development, users are accustomed to instant updates: the news feed updates without reloading, the interlocutor's status in a chat changes in real-time, and stock quotes arrive with millisecond delays. Traditional HTTP with its request-response model is inefficient for such tasks — it creates excessive load on the server and the device's battery.
The WebSocket protocol comes to the rescue. It establishes a permanent bidirectional connection between the client (an iOS/macOS app in Swift) and the server. In this article, we will look at two main ways to work with WebSocket in Swift: the native URLSessionWebSocketTask (iOS 13+ / macOS 10.15+) and the popular third-party library Starscream. You will learn how to create a real-time chat, configure automatic reconnection, and handle errors.
WebSocket Basics in Swift: URLSessionWebSocketTask
Starting with iOS 13 and macOS Catalina, Apple provides built-in WebSocket support through the URLSessionWebSocketTask class. This is a simple and secure way that does not require installing additional dependencies.
Creating and Configuring a Connection
First, let's create an instance of URLSession and set the configuration. Note: to work with WebSocket, you need to use the wss:// protocol (secure connection) or ws:// (unsecured, for development only).
import Foundation
class WebSocketManager: NSObject { private var webSocketTask: URLSessionWebSocketTask? private let session: URLSession override init() { let config = URLSessionConfiguration.default config.timeoutIntervalForRequest = 30 config.waitsForConnectivity = true self.session = URLSession(configuration: config, delegate: nil, delegateQueue: OperationQueue()) super.init() } func connect() { guard let url = URL(string: "wss://echo.websocket.org") else { return } webSocketTask = session.webSocketTask(with: url) webSocketTask?.resume() print("WebSocket connection established") receiveMessage() }}Sending and Receiving Messages
WebSocket supports two types of messages: String and binary (Data). Let's look at sending:
func sendMessage(_ text: String) { let message = URLSessionWebSocketTask.Message.string(text) webSocketTask?.send(message) { error in if let error = error { print("Send error: \\(error.localizedDescription)") } else { print("Message sent: \\(text)") } }}To receive messages, you need to implement a recursive call to the receive method:
private func receiveMessage() { webSocketTask?.receive { [weak self] result in switch result { case .success(let message): switch message { case .string(let text): print("Received text message: \\(text)") case .data(let data): print("Received binary data: \\(data.count) bytes") @unknown default: break } // Continue listening for the next messages self?.receiveMessage() case .failure(let error): print("Receive error: \\(error.localizedDescription)") // Here you can implement reconnection } }}Closing the Connection and Error Handling
Properly terminating a WebSocket connection is an important part of a production application:
func disconnect() { let closeCode = URLSessionWebSocketTask.CloseCode.normalClosure webSocketTask?.cancel(with: closeCode, reason: nil) p