Introduction: Why WebSocket is Important for a Swift Developer
Modern mobile and desktop applications increasingly require instant data exchange. Whether it's a chat, a stock trading terminal, an online game, or a push notification system — users expect real-time responses. The WebSocket protocol has become the standard for such tasks, and in the Swift ecosystem (iOS/macOS), its support is implemented natively through URLSessionWebSocketTask.
In this article, we will cover the full cycle of working with WebSocket in Swift: from setting up a connection to creating a full-fledged real-time application. You will learn how to avoid common mistakes, organize reconnection, and integrate WebSocket into the MVVM architecture.
WebSocket Basics in Swift: URLSessionWebSocketTask
Starting with iOS 13 and macOS 10.15, Apple provides a built-in class URLSessionWebSocketTask as part of Foundation. This eliminates the need to use third-party libraries (e.g., Starscream) for basic scenarios.
Creating a WebSocket Connection
To establish a connection, you need to create a URLSession session and set its configuration:
import Foundation
class WebSocketManager { private var webSocketTask: URLSessionWebSocketTask? private let session: URLSession init() { let config = URLSessionConfiguration.default config.timeoutIntervalForRequest = 30 config.waitsForConnectivity = true session = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue()) } func connect() { guard let url = URL(string: "wss://echo.websocket.org") else { return } webSocketTask = session.webSocketTask(with: url) webSocketTask?.resume() receiveMessage() }}Note the waitsForConnectivity — this allows automatic reconnection when the network becomes available.
Sending and Receiving Messages
WebSocket supports two types of messages: text (String) and binary (Data). Let's look at an example of 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)") } }}
func sendData(_ data: Data) { let message = URLSessionWebSocketTask.Message.data(data) webSocketTask?.send(message) { error in // error handling }}For receiving messages, a recursive call to receive is used:
private func receiveMessage() { webSocketTask?.receive { [weak self] result in switch result { case .success(let message): switch message { case .string(let text): print("Received text: \\(text)") case .data(let data): print("Received data: \\(data.count) bytes") @unknown default: break } // Continue listening self?.receiveMessage() case .failure(let error): print("Receive error: \\(error)") } }}Important: the receive call handles only one message. For continuous data reception, it must be called again inside the success closure.
Real-Time Application Architecture in Swift
For production use of WebSocket, the architecture must be carefully designed. It is recommended to use a service layer with a protocol and delegate to separate network logic from the UI.
WebSocket Service Protocol
protocol WebSocketServiceProtocol: AnyObject { var isConnected: Bool { get } func connect() func disconnect() f