مقدمة: لماذا Canary ليس عن الطيور؟
\n\nتخيل: أنت تطلق ميزة جديدة في التطبيق. كل شيء تم اختباره، CI/CD أخضر، مراجعة الكود تمت. بعد ساعة - سيل من التعليقات السلبية، أخطاء، أعطال. مألوف؟
\n\nفي عالم تطوير iOS، هناك حل أنيق - نشر Canary (النشر الكناري). إنها تقنية يحصل فيها نسبة صغيرة فقط من المستخدمين (عادة 1-5%) على الإصدار الجديد من الميزة. إذا كان كل شيء على ما يرام - ننشرها للجميع. إذا لم يكن كذلك - نتراجع بأقل ضرر.
\n\nفي هذه المقالة، سنشرح كيفية تنفيذ نشر Canary في Swift باستخدام أعلام الميزات و Firebase Remote Config. لا سحر - فقط كود عملي وممارسات حقيقية.
\n\nما هو نشر Canary ولماذا هو ضروري في Swift؟
\n\nالمصطلح جاء من صناعة التعدين: كان عمال المناجم يأخذون معهم طيور الكناري لتشير حالتها إلى تسرب الغاز. في تكنولوجيا المعلومات - نفس الشيء: مجموعة صغيرة من المستخدمين تعمل كـ "كناري"، تظهر ما إذا كان الإصدار الجديد يعمل.
\n\nلماذا هذا مهم لمطوري Swift؟
\n\n- \n
- الأمان: أنت لا تخاطر بجميع المستخدمين مرة واحدة \n
- اختبار A/B: يمكن مقارنة مقاييس الميزة القديمة والجديدة \n
- المرونة: التراجع يستغرق ثوانٍ وليس أيامًا \n
- التحكم: يمكن تفعيل الميزة بشكل انتقائي للمختبرين أو مستخدمي البيتا \n
في Swift نستخدم أعلام الميزات (feature flags) - وهي متغيرات منطقية تقوم بتشغيل/إيقاف الوظائف دون بناء جديد. ندمجها مع التكوين عن بعد (Firebase, LaunchDarkly) - ونحصل على نشر Canary كامل.
\n\nهندسة نشر Canary في Swift
\n\nدعونا نبني نظامًا بسيطًا ولكنه موثوق. سنحتاج إلى:
\n\n- \n
- موفر علم الميزة - خدمة تعطي حالة الميزة \n
- التكوين عن بعد - مخزن بعيد للأعلام (نستخدم Firebase) \n
- منطق التوزيع - من يدخل في مجموعة الكناري \n
- المراقبة - نجمع المقاييس وتقارير الأعطال \n
الخطوة 1: إعداد Firebase Remote Config
\n\nأولاً، نضيف Firebase إلى المشروع. إذا لم تكن قد فعلت ذلك بعد - اتبع الوثائق الرسمية. ثم ننشئ معلمات Remote Config:
\n\n- \n
new_checkout_enabled- يفعل عملية الدفع الجديدة \n new_checkout_percentage- النسبة المئوية للمستخدمين الذين سيرونها (0.0–1.0) \n new_checkout_deadline- تاريخ التفعيل الكامل (اختياري) \n
في وحدة تحكم Firebase، نضبط القيم الافتراضية: false, 0.0, nil.
الخطوة 2: موفر علم الميزة في Swift
\n\nننشئ بروتوكولًا وتنفيذًا. سيسمح هذا بالتبديل بسهولة بين Firebase والأعلام المحلية أو النماذج الوهمية للاختبارات.
\n\nimport Foundation\nimport FirebaseRemoteConfig\n\n// MARK: - Protocol\nprotocol FeatureFlagProvider {\n func isFeatureEnabled(_ feature: FeatureFlag) -> Bool\n func percentage(for feature: FeatureFlag) -> Double\n func fetchRemoteConfig(completion: @escaping (Bool) -> Void)\n}\n\n// MARK: - Feature Flags\nenum FeatureFlag: String, CaseIterable {\n case newCheckout = "new_checkout_enabled"\n case newCheckoutPercentage = "new_checkout_percentage"\n case newCheckoutDeadline = "new_checkout_deadline"\n \n var defaultValue: Any {\n switch self {\n case .newCheckout: return false\n case .newCheckoutPercentage: return 0.0\n case .newCheckoutDeadline: return "" // ISO8601 string\n }\n }\n}\n\n// MARK: - Firebase Implementation\nfinal class FirebaseFeatureFlagProvider: FeatureFlagProvider {\n private let remoteConfig = RemoteConf