البرمجة التفاعلية في Swift (iOS/macOS): Combine و RxSwift
البرمجة التفاعلية هي نموذج يعتمد على العمل مع تدفقات البيانات غير المتزامنة ونشر التغييرات. في نظام Apple البيئي لنظامي iOS و macOS، الأدوات الرئيسية هي إطار العمل الأصلي Combine (من iOS 13/macOS 10.15) والمكتبة الخارجية RxSwift. كلاهما يسمح بكتابة كود تصريحي، موجز، وسهل الاختبار، مما يريح المطور من "جحيم الاسترجاعات" وإدارة الحالة المعقدة.
المفاهيم الأساسية
- الناشر (Publisher) — مصدر البيانات الذي يمكنه إرسال القيم، الإكمال، أو الخطأ.
- المشترك (Subscriber) — يستقبل البيانات من الناشر ويستجيب لها.
- عامل التشغيل (Operator) — دالة لتحويل التدفق (map، filter، combineLatest، debounce وغيرها).
- الموضوع (Subject) — هجين يمكنه استقبال وإرسال الأحداث (PassthroughSubject، CurrentValueSubject).
- المجدول (Scheduler) — يحدد في أي تدفق (Thread) يتم تنفيذ الكود.
Combine: إطار Apple الأصلي
Combine مدمج في Foundation ويعمل مع Publisher و Subscriber. مثال على استخدامه لمعالجة حقل نصي:
import Combineimport UIKit
class ViewController: UIViewController { @IBOutlet weak var textField: UITextField! @IBOutlet weak var label: UILabel! private var cancellables = Set<AnyCancellable>()
override func viewDidLoad() { super.viewDidLoad() // إنشاء ناشر من NotificationCenter let publisher = NotificationCenter.default .publisher(for: UITextField.textDidChangeNotification, object: textField) .compactMap { ($0.object as? UITextField)?.text } .debounce(for: .milliseconds(500), scheduler: RunLoop.main) .removeDuplicates() // الاشتراك publisher .sink { [weak self] value in self?.label.text = "لقد أدخلت: \\(value)" } .store(in: &cancellables) }}في المثال، يتم استخدام debounce لتأخير الاستجابة للإدخال و removeDuplicates لتجاهل القيم المكررة.
RxSwift: بديل قوي
RxSwift هو نسخة من ReactiveX للغة Swift. يقدم مجموعة أوسع من عوامل التشغيل ويدعم إصدارات أقدم من iOS. نفس المثال باستخدام RxSwift:
import RxSwiftimport RxCocoa
class ViewController: UIViewController { @IBOutlet weak var textField: UITextField! @IBOutlet weak var label: UILabel! private let disposeBag = DisposeBag()
override func viewDidLoad() { super.viewDidLoad() textField.rx.text .orEmpty .debounce(.milliseconds(500), scheduler: MainScheduler.instance) .distinctUntilChanged() .subscribe(onNext: { [weak self] text in self?.label.text = "لقد أدخلت: \\(text)" }) .disposed(by: disposeBag) }}لاحظ استخدام rx.text (عبر RxCocoa) و distinctUntilChanged() — وهو مماثل لـ removeDuplicates في Combine.
عوامل التشغيل الرئيسية
map/compactMap— تحويل القيم.filter— يمرر فقط القيم التي تحقق الشرط.combineLatest— يدمج أحدث القيم من عدة تدفقات.zip— يدمج العناصر بشكل زوجي من عدة تدفقات.flatMap— يحول التدفق إلى تدفق آخر (يستخدم غالبًا لطلبات الشبكة).catch/retry— معالجة الأخطاء.