WebSocket Connection Integration for Mobile Chat
WebSocket gives full-duplex connection with minimal overhead compared to polling — correct choice for real chat. But mobile specifics add complexity layer: app goes to background, network switches from Wi-Fi to LTE, user locks screen. WebSocket client working well on desktop can lose connection in 30 seconds after background on iOS due to aggressive resource management.
Connection Management — Main Complexity
iOS. Background execution for WebSocket officially unsupported. If app needs receiving messages in background — only official path is push notifications (APNs). WebSocket stays active only while app in foreground. Attempts to keep connection alive via URLSessionWebSocketTask with background URLSessionConfiguration work unstably and violate Guidelines.
On iOS use URLSessionWebSocketTask (iOS 13+):
class WebSocketManager {
private var webSocketTask: URLSessionWebSocketTask?
func connect() {
let session = URLSession(configuration: .default, delegate: self, delegateQueue: .main)
webSocketTask = session.webSocketTask(with: URL(string: "wss://api.example.com/ws")!)
webSocketTask?.resume()
receiveMessage()
}
private func receiveMessage() {
webSocketTask?.receive { [weak self] result in
switch result {
case .success(let message):
self?.handleMessage(message)
self?.receiveMessage() // recursively
case .failure(let error):
self?.scheduleReconnect()
}
}
}
}
Android. OkHttp WebSocket — de facto standard. In background connection can be cut by JobScheduler or Doze Mode on Android 6+. Solution: WorkManager for periodic sync + FCM push for background message delivery. Keep WebSocket alive only when app in foreground, optionally — ForegroundService with notification.
val client = OkHttpClient.Builder()
.pingInterval(30, TimeUnit.SECONDS) // heartbeat
.build()
val request = Request.Builder().url("wss://api.example.com/ws").build()
val ws = client.newWebSocket(request, object : WebSocketListener() {
override fun onMessage(webSocket: WebSocket, text: String) {
// handle message
}
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
scheduleReconnect()
}
})
pingInterval(30) important — without heartbeat connection breaks by intermediate proxies after 60–90 seconds silence.
Reconnection with Exponential Backoff
Reconnection on break — mandatory logic. Simple retry via 1 second creates request storm on server fall. Correct approach — exponential backoff with jitter:
private var reconnectDelay = 1000L
fun scheduleReconnect() {
viewModelScope.launch {
delay(reconnectDelay + Random.nextLong(500))
reconnectDelay = minOf(reconnectDelay * 2, 30_000L)
connect()
}
}
fun onConnected() {
reconnectDelay = 1000L
}
Flutter: web_socket_channel package — wrapper over native implementations. For production level recommend stomp_dart_client if server uses STOMP, or custom manager with same reconnect principles.
Authentication
WebSocket connection authenticated once on establishment — via Authorization header in handshake or first message after connect (auth frame). JWT token may expire during session — need token refresh and reconnection logic.
What's Included
Implement WebSocket client per platform with reconnect logic, heartbeat, network change handling (NetworkCallback on Android, NWPathMonitor on iOS). If fallback to push needed — integrate APNs/FCM for background message delivery.
Timeline: 4–8 days for full implementation considering network edge-cases.







