Event Bus ونمط Observer في Swift (iOS/macOS)
\n\nفي التطوير لأنظمة iOS و macOS، غالباً ما تنشأ الحاجة لتنظيم تفاعل ضعيف الاقتران بين مكونات التطبيق. نمط Observer وتنفيذه في شكل Event Bus (ناقل الأحداث) هو أداة قوية لحل هذه المشكلة. في هذه المقالة، سنشرح بالتفصيل كيفية تنفيذ Event Bus بلغة Swift، باستخدام الإمكانيات الحديثة للغة (Generics, closures, weak references).
\n\nما هو نمط Observer؟
\n\nنمط Observer (المراقب) يحدد علاقة "واحد إلى متعدد" بين الكائنات. عندما يغير كائن واحد (الناشر) حالته، يتم إخطار جميع الكائنات التابعة له (المشتركون) وتحديثها تلقائياً. هذا يسمح بتجنب الاقتران الصلب ويبسط صيانة الكود.
\n\nفي Swift، التطبيقات الأصلية لـ Observer هي:
\n- \n
NotificationCenter— ناقل إشعارات عالمي، مدمج في Foundation. \n - Key-Value Observing (KVO) — آلية مراقبة خصائص الكائنات. \n
- Combine framework (iOS 13+) — إطار عمل تفاعلي يدعم الناشرين والمشتركين. \n
ومع ذلك، غالباً ما يكون هناك حاجة لتنفيذ أخف وأكثر أماناً من حيث الأنواع، خاصة عند العمل مع كود قديم أو في مشاريع لا يتوفر فيها Combine.
\n\nEvent Bus: تنفيذ بسيط ومرن
\n\nEvent Bus هو مدير أحداث مركزي يسمح بالاشتراك في الأحداث وإرسالها دون معرفة مباشرة بالمشتركين. سنقوم بتنفيذه باستخدام الأدوية (Generics) والمراجع الضعيفة لتجنب تسرب الذاكرة.
\n\nالتنفيذ الأساسي لـ Event Bus
\n\nimport Foundation\n\n// بروتوكول لجميع الأحداث\nprotocol Event { }\n\n// أنواع أحداث محددة\nstruct UserLoggedInEvent: Event {\n let userId: String\n let userName: String\n}\n\nstruct DataLoadedEvent: Event {\n let data: [String]\n}\n\n// كلاس Event Bus\nfinal class EventBus {\n static let shared = EventBus()\n private init() {}\n \n // قاموس لتخزين المشتركين\n private var subscribers = [ObjectIdentifier: [Any]]()\n \n // الاشتراك في حدث\n func subscribe<T: Event>(observer: AnyObject, handler: @escaping (T) -> Void) {\n let key = ObjectIdentifier(T.self)\n let wrapped = Subscription(observer: observer, handler: handler as Any)\n \n if subscribers[key] != nil {\n subscribers[key]?.append(wrapped)\n } else {\n subscribers[key] = [wrapped]\n }\n }\n \n // إلغاء الاشتراك من جميع الأحداث لمراقب معين\n func unsubscribe(observer: AnyObject) {\n for key in subscribers.keys {\n subscribers[key]?.removeAll(where: { subscription in\n if let sub = subscription as? Subscription {\n return sub.observer === observer\n }\n return false\n })\n }\n }\n \n // نشر حدث\n func publish<T: Event>(event: T) {\n let key = ObjectIdentifier(T.self)\n guard let subs = subscribers[key] else { return }\n \n for subscription in subs {\n if let sub = subscription as? Subscription {\n if let handler = sub.handler as? (T) -> Void {\n handler(event)\n }\n }\n }\n }\n}\n\n// هيكل مساعد لتخزين الاشتراكات\nprivate struct Subscription {\n weak var observer: AnyObject?\n let handler: Any\n \n init(observer: AnyObject, handler: Any) {\n self.observer = observer\n self.handler = handler\n }\n}\n\n\nاستخدام Event Bus
\n\n// ViewController يشترك في الأحداث\nclass LoginViewController: UIViewController {\n \n override func viewDidLoad() {\n super.viewDidLoad()\n \n // الاشتراك في الحدث