نقل البيانات بين الشاشات في iOS: من المبتدئ إلى المحترف
\n\nكل من بدأ البرمجة لنظام iOS، عاجلاً أم آجلاً، يصطدم بجدار: "كيف أنقل سلسلة نصية أو مصفوفة أو كائن إلى شاشة أخرى؟". السؤال شائع جداً لدرجة أنه حصد آلاف المشاهدات وعشرات الإجابات على Stack Overflow. وهذا ليس بدون سبب — فمعمارية iOS مبنية بحيث تعيش كل شاشة (ViewController) حياتها الخاصة، ويجب نقل البيانات بينها بوعي.
\n\nفي هذه المقالة، سنشرح 5 طرق أساسية لنقل البيانات بين view controllers في Swift. ستتعرف متى تستخدم segue، ومتى تستخدم delegates أو closures، ولماذا NotificationCenter ليس شراً دائماً. هيا بنا!
\n\nلماذا هذه مشكلة أصلاً؟
\nيستخدم iOS معمارية MVC (Model-View-Controller) الصارمة. كل ViewController مسؤول عن جزء الشاشة الخاص به وليس لديه وصول مباشر إلى بيانات المتحكم المجاور. هذا مصمم للأمان والنمطية، ولكن بالنسبة للمطور، هذا يعني أنك بحاجة إلى "تمرير" البيانات بشكل صريح من النقطة أ إلى النقطة ب.
\n\nتخيل: لديك قائمة بالمستخدمين، وتريد عرض معلومات مفصلة عند النقر على خلية. تنقر — تفتح شاشة جديدة. كيف تنقل معرف المستخدم؟ هنا يبدأ السحر.
\n\nالطريقة 1: نقل البيانات عبر Storyboard Segue (الأسهل)
\nالطريقة الأكثر مباشرة هي استخدام prepare(for:sender:). تقوم ببساطة بتحضير البيانات قبل الانتقال. مثالية للتطبيقات البسيطة والنماذج الأولية.
// في ViewController المصدر\noverride func prepare(for segue: UIStoryboardSegue, sender: Any?) {\n if segue.identifier == "showDetail" {\n guard let destinationVC = segue.destination as? DetailViewController else { return }\n destinationVC.userID = 42 // نقل رقم\n destinationVC.userName = "آنا" // نقل سلسلة نصية\n }\n}\n\n// في DetailViewController\nvar userID: Int = 0\nvar userName: String = ""\n\n\nالإيجابيات: أقل كود، كل شيء في مكان واحد. السلبيات: ارتباط صارم بـ Storyboard، صعوبة في الاختبار، سهولة الخلط بين المعرفات.
\n\nالطريقة 2: التنقل البرمجي مع نقل البيانات (مستوى متقدم)
\nعندما تمل من segues وتريد التحكم بكل شيء من الكود، استخدم مثيلاً مباشراً للمتحكم.
\n\n// في ViewController المصدر\nfunc showDetail() {\n let storyboard = UIStoryboard(name: "Main", bundle: nil)\n guard let detailVC = storyboard.instantiateViewController(withIdentifier: "DetailVC") as? DetailViewController else { return }\n \n detailVC.userID = 42\n detailVC.userName = "آنا"\n \n navigationController?.pushViewController(detailVC, animated: true)\n}\n\n\nهذه الطريقة تمنحك تحكماً كاملاً: يمكنك تحميل البيانات من الشبكة، والتحقق منها، ثم الانتقال. ممتازة للمنطق المعقد.
\n\nالطريقة 3: Delegates (نمط التفويض) — ردود الفعل
\nماذا تفعل إذا كنت بحاجة لنقل البيانات للخلف؟ على سبيل المثال، اختار المستخدم تاريخاً على الشاشة الثانية، ونريد إعادته إلى الشاشة الأولى. هنا يأتي دور delegate.
\n\nالخطوة 1: إنشاء بروتوكول
\nprotocol DatePickerDelegate: AnyObject {\n func didSelectDate(_ date: Date)\n}\n\n\nالخطوة 2: إضافة delegate إلى المتحكم الفرعي
\nclass DatePickerViewController: UIViewController {\n weak var delegate: DatePickerDelegate?\n \n @IBAction func saveButtonTapped(_ sender: UIButton) {\n let selectedDate = datePicker.date\n delegate?.didSelectDate(selectedDate)\n dismiss(animated: true)\n }\n}\n\n\nالخطوة 3: تنفيذ delegate في المتحكم الأب
\nclass MainViewController: UIViewController, DatePickerDelegate {\n func didSelectDate(_ date: Date) {\n print("تم استلام التاريخ: \\(date)")\n // تحديث واجهة المستخدم\n }\n \n override f