Introduction to WebSocket in Swift: from theory to practice
Modern mobile and desktop applications increasingly require instant data transfer. Whether it's a chat, stock exchange terminal, online game, or monitoring system — users expect information to update in real time without constantly refreshing the screen. This is where the WebSocket protocol comes to the rescue.
Unlike a classic HTTP request, which works on a "request-response" principle, WebSocket establishes a permanent bidirectional connection between the client and server. This means the server can send data to the client at any time without a prior request. In the Apple ecosystem, both native solutions (URLSessionWebSocketTask) and popular third-party libraries, such as Starscream, are available for working with WebSocket.
In this article, we will look at how to integrate WebSocket into your Swift application for iOS and macOS, write working code examples, and review best practices.
1. Native WebSocket implementation via URLSession
Starting with iOS 13 and macOS 10.15, Apple provides built-in WebSocket support through the URLSessionWebSocketTask class. This is the simplest and recommended method, requiring no installation of third-party dependencies.
Creating and configuring the connection
First, you need to create an instance of URLSession and set its configuration. Then we form a URL with the ws:// protocol (or wss:// for a secure connection) and create a WebSocket task.
import Foundation
class WebSocketManager: NSObject { private var webSocketTask: URLSessionWebSocketTask? private let session: URLSession override init() { let config = URLSessionConfiguration.default 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() receiveMessage() } func disconnect() { webSocketTask?.cancel(with: .normalClosure, reason: nil) }}Sending and receiving messages
WebSocket supports sending both text (URLSessionWebSocketTask.Message.string) and binary data (.data). To receive messages, you need to recursively call the receive method, as it processes only one message at a time.
extension WebSocketManager { func sendMessage(_ text: String) { let message = URLSessionWebSocketTask.Message.string(text) webSocketTask?.send(message) { error in if let error = error { print("Send error: \\(error.localizedDescription)") } } } 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: print("Unknown message type") } // Recursively continue listening self?.receiveMessage() case .failure(let error): print("Receive error: \\(error.localizedDescription)") } } }}Note the recursive call to receiveMessage() inside the success closure — this ensures your application will continuously receive new data.