Introduction à WebSocket en Swift : de la théorie à la pratique avancée
Dans le développement mobile moderne, l'interaction en temps réel est devenue non seulement un atout, mais une nécessité. Les utilisateurs s'attendent à ce que les chats, les flux d'actualités, les cotations et les notifications se mettent à jour instantanément, sans gestes superflus de pull-to-refresh. WebSocket est le protocole qui permet d'établir une connexion bidirectionnelle permanente entre le client (votre application en Swift) et le serveur.
Dans cet article, nous dépasserons le simple "Hello, World!" et explorerons les techniques avancées de travail avec WebSocket en Swift pour les plateformes iOS et macOS. Vous apprendrez à configurer une connexion fiable, à gérer des scénarios complexes (perte de connexion, reconnexion, mode arrière-plan) et à construire une architecture capable de supporter la charge d'une véritable application boursière ou d'un chat avec des milliers d'utilisateurs.
Nous aborderons à la fois le natif URLSessionWebSocketTask (iOS 13+) et la bibliothèque populaire Starscream pour les versions antérieures. Le code sera entièrement fonctionnel et prêt à être intégré.
1. Configuration avancée du client WebSocket en Swift
Commençons par les bases. Au lieu d'une connexion triviale, nous allons créer un gestionnaire qui gérera le cycle de vie de la connexion, traitera les erreurs et assurera la reconnexion automatique.
1.1. Gestionnaire avec reconnexion automatique
Implémentons une classe WebSocketManager qui utilise URLSessionWebSocketTask. La caractéristique clé est un délai exponentiel entre les tentatives de reconnexion, afin de ne pas surcharger le serveur.
import Foundation
final class WebSocketManager: NSObject { private var webSocketTask: URLSessionWebSocketTask? private var session: URLSession? private var isConnected = false private var reconnectAttempt = 0 private let maxReconnectDelay: TimeInterval = 30 // Délai maximum de 30 sec private let baseDelay: TimeInterval = 2 // Délai de base de 2 sec var onMessageReceived: ((String) -> Void)? var onConnectionStateChanged: ((Bool) -> Void)? func connect(url: URL) { session = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue()) let request = URLRequest(url: url, timeoutInterval: 10) webSocketTask = session?.webSocketTask(with: request) webSocketTask?.resume() receiveMessage() } private func reconnect() { guard let url = webSocketTask?.originalRequest?.url else { return } let delay = min(baseDelay * pow(2.0, Double(reconnectAttempt)), maxReconnectDelay) print("Reconnexion dans \\(delay) secondes... (Tentative \\(reconnectAttempt + 1))") DispatchQueue.global().asyncAfter(deadline: .now() + delay) { [weak self] in self?.connect(url: url) self?.reconnectAttempt += 1 } } private func receiveMessage() { webSocketTask?.receive { [weak self] result in switch result { case .success(let message): switch message { case .string(let text): self?.onMessageReceived?(text) case .data(let data): if let text = String(data: data, encoding: .utf8) { self?.onMessageReceived?(text) } @unknown default: break } self?.reconnectAttempt = 0 // Réinitialisation du compteur en cas de succès self?.receiveMessage() // Continuer à écouter case .failure(let error): print("Erreur de réception : \\(error.localizedDescription)") self?.disconnect() self?.reconnect() } } } func send(text: String) {