WebSocket Protocol
What are WebSockets?
WebSockets provide full-duplex communication over a single TCP connection. Unlike HTTP request-response, WebSockets allow both the client and server to send messages at any time.
This makes WebSockets ideal for real-time features like chat apps, live dashboards, multiplayer games, and collaborative editing.
WebSocket Handshake
WebSockets start as HTTP requests with an upgrade header:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
If the server supports WebSockets, it responds with 101 Switching Protocols.
Use Cases
WebSockets are perfect for:
- Real-time chat and messaging
- Live sports scores and financial data
- Multiplayer game state synchronization
- Collaborative document editing
- Live notifications and alerts
- IoT device communication
When NOT to Use WebSockets
Avoid WebSockets when:
- You need simple request-response patterns (use REST)
- Data is rarely updated (use polling or push notifications)
- You need automatic scaling (WebSockets maintain state)
- Mobile data usage is a concern (persistent connections)
URLSessionWebSocketTask
Creating a WebSocket Task
iOS provides URLSessionWebSocketTask for WebSocket communication:
let url = URL(string: "wss://echo.websocket.org")!
let session = URLSession(configuration: .default)
let task = session.webSocketTask(with: url)
Connecting
task.resume()
print("WebSocket connected")
Receiving Messages
func receiveMessages() async {
do {
let message = try await task.receive()
switch message {
case .string(let text):
print("Text: \(text)")
case .data(let data):
print("Data: \(data.count) bytes")
@unknown default:
break
}
} catch {
print("Receive error: \(error)")
}
}
// Listen for messages in a loop
func listenForMessages() async {
while task.state == .running {
await receiveMessages()
}
}
Sending Messages
// Send text
let message = URLSessionWebSocketTask.Message.string("Hello Server")
task.send(message) { error in
if let error = error {
print("Send error: \(error)")
}
}
// Send data
let jsonData = try JSONEncoder().encode(ChatMessage(text: "Hello"))
task.send(.data(jsonData)) { error in
if let error = error {
print("Send error: \(error)")
}
}
Ping/Pong Keepalive
task.sendPing { error in
if let error = error {
print("Ping failed: \(error)")
} else {
print("Pong received")
}
}
Disconnecting
task.cancel(with: .goingAway, reason: nil)
Real-Time Communication
WebSocket Manager
Create a reusable WebSocket manager:
actor WebSocketManager {
private var task: URLSessionWebSocketTask?
private let session: URLSession
private var url: URL
init(url: URL) {
self.url = url
self.session = URLSession(configuration: .default)
}
func connect() async throws {
task = session.webSocketTask(with: url)
task?.resume()
}
func send(_ message: String) async throws {
try await task?.send(.string(message))
}
func receive() async throws -> String {
let message = try await task?.receive()
switch message {
case .string(let text): return text
case .data(let data): return String(data: data, encoding: .utf8) ?? ""
default: return ""
}
}
func disconnect() {
task?.cancel(with: .goingAway, reason: nil)
}
}
Reconnection Logic
Implement automatic reconnection with exponential backoff:
func connectWithReconnect() async {
var attempt = 0
let maxAttempts = 10
while attempt < maxAttempts {
do {
try await connect()
await listenForMessages()
return
} catch {
attempt += 1
let delay = min(pow(2.0, Double(attempt)), 30.0)
try? await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
}
}
}
Message Protocol
Define a message protocol for type safety:
enum ChatMessage: Codable {
case text(String)
case userJoined(String)
case userLeft(String)
case typing(String)
}
Handling Disconnections
Detect and handle disconnections:
func listenForMessages() async {
while task?.state == .running {
do {
let message = try await task?.receive()
// Process message
} catch {
// Connection lost - attempt reconnect
await connectWithReconnect()
return
}
}
}
Quiz
1. What protocol upgrade starts a WebSocket connection?
2. What does URLSessionWebSocketTask.receive() return?
3. What is ping/pong used for in WebSockets?
4. How do you close a WebSocket connection gracefully?
Flashcards
Question
What is a WebSocket?
Click to reveal answer
Answer
A protocol for full-duplex communication over a single TCP connection, allowing both client and server to send messages at any time.
Question
How do you create a WebSocket connection in iOS?
Click to reveal answer
Answer
Use URLSession.webSocketTask(with: url) to create a task, then call .resume() to connect.
Question
What message types do WebSockets support?
Click to reveal answer
Answer
Text frames (strings) and binary frames (Data). URLSessionWebSocketTask.Message has .string and .data cases.
Question
When should you use WebSockets vs REST?
Click to reveal answer
Answer
Use WebSockets for real-time bidirectional communication. Use REST for simple request-response patterns.
Revision Notes
Key Takeaways
- 1. WebSockets provide full-duplex communication for real-time features
- 2. URLSessionWebSocketTask handles WebSocket connections on iOS
- 3. Ping/pong frames maintain connection health
- 4. Implement reconnection with exponential backoff for reliability
- 5. Use WebSockets for chat, live data, and collaborative features
Interview Tips
- • Explain the WebSocket handshake process (HTTP Upgrade to 101)
- • Know when to use WebSockets vs Server-Sent Events vs REST
- • Describe reconnection strategies with exponential backoff
- • Discuss handling message ordering and delivery guarantees
Cheat Sheet
WebSockets: Full-duplex over single TCP connection.
URLSessionWebSocketTask: .resume() to connect, .receive() for messages, .send() to send.
Ping/Pong: Keepalive mechanism to detect dead connections.
Reconnection: Exponential backoff (1s, 2s, 4s) with max attempts.
Disconnect: .cancel(with: .goingAway, reason: nil).