مقدمة إلى WebSocket بلغة Swift: من النظرية إلى التطبيق المتقدم
في تطوير التطبيقات المحمولة الحديثة، أصبح التفاعل الفوري (real-time) ليس مجرد ميزة إضافية، بل ضرورة. يتوقع المستخدمون أن يتم تحديث الدردشات، خلاصات الأخبار، أسعار الأسهم، والإشعارات بشكل فوري، دون الحاجة إلى إيماءات إضافية مثل السحب للتحديث (pull-to-refresh). WebSocket هو البروتوكول الذي يسمح بإنشاء اتصال دائم ثنائي الاتجاه بين العميل (تطبيقك بلغة Swift) والخادم.
في هذه المقالة، سنتجاوز حدود مثال "Hello, World!" البسيط وسنتناول التقنيات المتقدمة للعمل مع WebSocket بلغة Swift لمنصتي iOS وmacOS. ستتعلم كيفية إعداد اتصال موثوق، ومعالجة السيناريوهات المعقدة (فقدان الاتصال، إعادة الاتصال، الوضع الخلفي)، وبناء بنية تحتية تتحمل ضغط تطبيق تداول أسهم حقيقي أو دردشة تضم آلاف المستخدمين.
سنستعرض كلاً من الأداة الأصلية URLSessionWebSocketTask (لنظام iOS 13+) والمكتبة الشائعة Starscream للإصدارات الأقدم. سيكون الكود عملياً بالكامل وجاهزاً للتكامل.
1. إعداد متقدم لعميل WebSocket بلغة Swift
لنبدأ من الأساس. بدلاً من الاتصال البسيط، سننشئ مديراً يدير دورة حياة الاتصال، ويعالج الأخطاء، ويدعم إعادة الاتصال التلقائي (reconnect).
1.1. مدير مع إعادة اتصال تلقائي (Reconnect)
سنقوم بتنفيذ كلاس WebSocketManager الذي يستخدم URLSessionWebSocketTask. الميزة الرئيسية هي التأخير الأسي (exponential backoff) بين محاولات إعادة الاتصال، لتجنب تحميل الخادم بشكل زائد.
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 // أقصى تأخير 30 ثانية private let baseDelay: TimeInterval = 2 // التأخير الأساسي 2 ثانية 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("إعادة الاتصال خلال \\(delay) ثانية... (محاولة \\(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 // إعادة تعيين العداد عند الاستلام الناجح self?.receiveMessage() // الاستمرار في الاستماع case .failure(let error): print("خطأ في الاستلام: \\(error.localizedDescription)") self?.disconnect() self?.reconnect() } } } func send(text: String) {