Introduction: Why Canary Isn't About Birds?
\n\nImagine: you release a new feature in your app. Everything is tested, CI/CD is green, code review is passed. An hour later — a flood of negative reviews, bugs, crashes. Sound familiar?
\n\nIn the world of iOS development, there is an elegant solution — Canary deployments. This is a technique where only a small percentage of users (usually 1–5%) receive the new version of a feature. If everything is fine — we roll it out to everyone. If not — we roll back with minimal damage.
\n\nIn this article, we'll break down how to implement Canary deployments in Swift using feature flags and Firebase Remote Config. No magic — just working code and real-world practices.
\n\nWhat is a Canary deployment and why do you need it in Swift?
\n\nThe term comes from the mining industry: miners would take canaries with them so that the birds' condition would signal a gas leak. In IT — the same thing: a small group of users serves as the "canary," showing whether the new version works.
\n\nWhy is this important for Swift developers?
\n\n- \n
- Safety: You don't risk all users at once \n
- A/B Testing: You can compare metrics of the old and new feature \n
- Flexibility: Rollback takes seconds, not days \n
- Control: You can selectively enable a feature for testers or beta users \n
In Swift, we use feature flags — these are boolean variables that turn functionality on/off without a new build. We combine them with remote configuration (Firebase, LaunchDarkly) — and we get a full-fledged Canary deployment.
\n\nCanary Deployment Architecture in Swift
\n\nLet's build a simple but reliable system. We'll need:
\n\n- \n
- Feature Flag Provider — a service that returns the feature status \n
- Remote Config — a remote storage for flags (we'll use Firebase) \n
- Distribution Logic — who gets into the canary group \n
- Monitoring — collect metrics and crash reports \n
Step 1: Setting up Firebase Remote Config
\n\nFirst, add Firebase to your project. If you haven't done this yet, follow the official documentation. Then create Remote Config parameters:
\n\n- \n
new_checkout_enabled— enables the new checkout \n new_checkout_percentage— percentage of users who will see it (0.0–1.0) \n new_checkout_deadline— date for full rollout (optional) \n
In the Firebase console, set default values: false, 0.0, nil.
Step 2: Feature Flag Provider in Swift
\n\nCreate a protocol and implementation. This will make it easy to switch between Firebase, local flags, or mocks for testing.
\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