البرمجة التفاعلية في Swift: Combine و RxSwift
\n\nالبرمجة التفاعلية هي نموذج يركز على تدفقات البيانات غير المتزامنة ونشر التغييرات. في نظام Swift البيئي (iOS/macOS)، الأدوات الرئيسية لتنفيذ هذا النموذج هي إطار Combine (المدمج في Apple SDK) والمكتبة الخارجية RxSwift. تتيح كلتا الأداتين وصف معالجة الأحداث بطريقة تصريحية، مما يقلل من عدد دوال الاسترجاع (callback) ويجعل الكود أكثر قابلية للقراءة وقابلية للتنبؤ.
\n\nفي هذه المقالة، سنستعرض المفاهيم الأساسية للبرمجة التفاعلية، ونقارن بين Combine و RxSwift، ونقدم أمثلة عملية لنظامي iOS و macOS.
\n\nالمفاهيم الأساسية
\nيقوم النهج التفاعلي على ثلاث كيانات:
\n\n- \n
- الناشر (Publisher/Observable) — مصدر البيانات الذي يولد الأحداث (القيم، الأخطاء، الإكمال). \n
- المشترك (Subscriber/Observer) — المستهلك الذي يستجيب للأحداث. \n
- المُشغّل (Operator) — دالة تحول تدفق البيانات (التصفية، التحويل، الدمج). \n
مثال على ناشر بسيط في Combine:
\nimport Combine\n\nlet publisher = Just("Hello, Reactive World!")\nlet subscription = publisher.sink { value in\n print(value)\n}\n// الإخراج: Hello, Reactive World!\n\nCombine مقابل RxSwift: مقارنة
\nكلا الإطارين يحلان مهامًا متشابهة، لكن بينهما اختلافات:
\n\n- \n
- Combine — إطار أصلي من Apple (iOS 13+، macOS 10.15+). يستخدم كتابة صارمة ويتكامل مع SwiftUI و URLSession. \n
- RxSwift — مكتبة متعددة المنصات مع مجتمع كبير. تدعم إصدارات iOS الأقدم (بدءًا من iOS 8). \n
- الأداء: Combine مُحسّن لمعالجات Apple Silicon، بينما RxSwift قد يكون أكثر مرونة في السيناريوهات المخصصة. \n
أمثلة برمجية لنظامي iOS/macOS
\n\n1. معالجة تفاعلية لحقل نصي (Combine + UIKit)
\nالاشتراك في تغييرات النص في UITextField:
\nimport UIKit\nimport Combine\n\nclass ViewController: UIViewController {\n @IBOutlet weak var textField: UITextField!\n private var cancellables = Set<AnyCancellable>()\n \n override func viewDidLoad() {\n super.viewDidLoad()\n \n NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: textField)\n .compactMap { ($0.object as? UITextField)?.text }\n .debounce(for: .milliseconds(500), scheduler: RunLoop.main)\n .sink { text in\n print("المستخدم أدخل: \\(text)")\n // هنا يمكن إجراء طلب شبكة أو تحديث واجهة المستخدم\n }\n .store(in: &cancellables)\n }\n}\n\n2. دمج عدة تدفقات (RxSwift لنظام macOS)
\nمثال على دمج تسلسلين observable:
\nimport RxSwift\n\nlet disposeBag = DisposeBag()\n\nlet firstObservable = Observable.of(1, 2, 3)\nlet secondObservable = Observable.of("A", "B", "C")\n\nObservable.zip(firstObservable, secondObservable)\n .subscribe(onNext: { (number, letter) in\n print("\\(number) -> \\(letter)")\n })\n .disposed(by: disposeBag)\n\n// الإخراج:\n// 1 -> A\n// 2 -> B\n// 3 -> C\n\n3. معالجة طلب شبكة باستخدام Combine
\nاستخدام URLSession لتحميل البيانات:
\nimport Combine\nimport Foundation\n\nstruct Post: Decodable {\n let id: Int\n let title: String\n}\n\nfunc fetchPosts() -> AnyPublisher<[Post], Error> {\n let url = URL(string: "https://jsonplaceholder.typicode.com/posts")!\n return URLSession.shared.dataTaskPublisher(for: url)\n .map(\\.data)\n .decode(type: [Post].self, decoder: JSONDecoder())\n .eraseToAnyPublisher()\n}\n