-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Feature Flags
Is the Firefox iOS codebase, we define a feature flag as a variable, inside a feature, that controls the status of the feature in the application.
Feature flags should logically be part of their own features, even if that's the only variable in that feature. - ie. Should not be part of a generalAppFeature, or a featureFlagFeature.
Feature flags are all controlled by the FeatureFlagManager singleton. To access the singleton, you must make a class conform to the FeatureFlaggable protocol, which will give access to the featureFlags variable.
class BibimbapViewModel: FeatureFlaggable {
var isNewMenuAvailable: Bool {
return featureFlags.isFeatureEnabled(.newBibimbapMenu, checking: .buildOnly)
}
}| Name | Description | User Togglable |
|---|---|---|
| Core | Core features are features that are used for developer purposes and are not directly user impacting. | No |
| Nimbus | A nimbus feature is a feature whose configuration comes from Nimbus. | Possibly |
The vast majority of feature flags should be Nimbus flags, rather than Core flags.
| Interface | Purpose |
|---|---|
isCoreFeatureEnabled(...) |
Checking where a Core feature is enabled. |
isFeatureEnabled(...) |
Checking whether a boolean based Nimbus feature is enabled. |
getCustomState<T>(...) |
Checking the status of a non-boolean based Nimbus feature. |
set(...) |
Saving a boolean based Nimbus feature user preference to UserDefaults. |
set<T: FlaggableFeatureOptions>(...) |
Saving a non-boolean based Nimbus feature user preference to UserDefaults. |
One of the complexities of feature flags is that while Nimbus may have a default, a user may turn something off. Regardless of whether or not the user is in an experiment, their preferences should be respected. To accomplish this, the previously listed interfaces that check a feature status include a specific checking parameter. This has three options which should cover 100% of use cases for needing to check the status of a feature.
-
buildOnly- this will only check Nimbus configuration for status -
userOnly- this will check UserDefaults to see if the user has a preference. If they do, that is what will be returned. If they do not, then the Nimbus configuration is queried for status -
buildAndUser- this will a mix of both.
To add a feature to Nimbus, please read Nimbus Feature. Once this is done, add a variable to that feature named something indicative of a status. Here is an example of what that might look like
...
isEnabled:
description: >
Whether or not the feature is enabled.
type: Boolean
default: falseTo add the flag in the app (for example, for the newBibimbapMenu flag), follow these steps:
- Add
case newBibimbapMenuto theNimbusFeatureFlagIDenum. - Add this new case to the
NimbusFlaggableFeaturestruct. a. If the user will have a setting to interact with for the feature, you should add this such that it returns aPrefsKeys.FeatureFlagskey, which you will have to also add. b. If the user doesn't have a setting for the feature, you should add it to thereturn nilpart of the switch statement. - In the
NimbusFeatureFlagLayerclass, you should add a case for your new feature, as well as the function it will call
...
switch featureID {
case .newBibimbapMenu:
return checkBibimbapFeature(for: featureID, from: nimbus)
...
private func checkBibimbapFeature(
for featureID: NimbusFeatureFlagID,
from nimbus: FxNimbus
) -> Bool {
let config = nimbus.features.bibimbapFeature.value()
switch featureID {
case .newBibimbapMenu: return config.newBibimbapMenu
default: return false
}
}