Feature flags in the iOS SDK
Ce contenu n'est pas encore disponible dans votre langue.
Pushwoosh.configure.featureFlags gives your iOS app the feature flags configured for the application in the Control Panel. Pushwoosh evaluates the flag rules on the server for the current device and user, and the SDK returns the result: whether the flag is on for this device and which property values apply.
Prerequisites
Anchor link to- Pushwoosh iOS SDK 7.2.10 or later, integrated as described in the iOS SDK guide. Feature flags ship in the required
PushwooshFrameworkandPushwooshCoremodules, so you don’t add another module. They work in the iOS app itself. App extensions, such as a Notification Service Extension, never request flags. - At least one flag created in Audience → Feature Flags. You need its Key and the keys and types of its properties. Use the keys exactly as they appear there, letter case included. For example,
get("New_Checkout")doesn’t find a flag with the keynew_checkout. - If the app sends SDK requests through a reverse proxy, its URL must end with
json/1.3/. Otherwise the SDK doesn’t request flags, and every flag falls back to your default.
Read a flag
Anchor link toAlways pass a default. It applies until the SDK has flags for the device, and whenever the flag doesn’t exist, is archived, or the plan doesn’t include feature flags.
import PushwooshFramework
let flags = Pushwoosh.configure.featureFlags
if let checkout = flags.get("new_checkout"), checkout.isEnabled { showNewCheckout( buttonColor: checkout.string(forProperty: "button_color", default: "blue") ?? "blue", maxItems: Int(checkout.number(forProperty: "max_items", default: 3)) )} else { showOldCheckout()}To check only whether the flag is on, use isEnabled:
let isNewCheckout = Pushwoosh.configure.featureFlags.isEnabled("new_checkout", default: false)In Objective-C, call [PushwooshConfig featureFlags].
What a flag returns
Anchor link toget(_:) returns a PWFeatureFlag object, or nil when the SDK has no flags for the device yet or the flag isn’t in the answer. An archived flag is not in the answer. get reads only the stored flags and never waits for the network. The object has these properties:
| Property | Type | Description |
|---|---|---|
key | String | The flag key. |
isEnabled | Bool | Whether the flag is on for this device. |
ruleId | String | The rule that decided the result, for example r_1a2b3c4d. default when no rule matched the device, and an empty string when the flag is killed. |
Read property values with the method that matches the property type. When the flag is off, the properties hold the default values set in the Control Panel.
| Method | Returns |
|---|---|
string(forProperty:default:) | The value of a String property, or your default. |
number(forProperty:default:) | The value of a Number property as a Double, or your default. |
bool(forProperty:default:) | The value of a Boolean property, or your default. |
jsonObject(forProperty:) | The value of a JSON property that holds an object, as a dictionary, or nil. |
jsonArray(forProperty:) | The value of a JSON property that holds an array, or nil. |
Each method returns your default, or nil for the JSON methods, when the property is missing or holds a value of another type.
isEnabled(_:default:) returns the default when get(_:) returns nil.
Keep flags up to date
Anchor link toThe SDK stores the last answer on the device for up to 24 hours. The stored flags are valid only for the User ID and the Application Code of the app they were requested for. The SDK refreshes them:
- When a session starts, that is, when the app becomes active for the first time or after at least 30 minutes in the background.
- Every hour while the app is in the foreground.
- After the device registers for push notifications, if the previous request was sent before the registration.
- Right after the User ID changes with
setUserId. The stored flags of the previous user are discarded, so a new user never sees them.
To react to new values, add a listener that adopts PWFeatureFlagsUpdateListener. Its featureFlagsDidUpdate() is called on the main thread after a refresh that changed any flag. The SDK holds listeners weakly. Keep a strong reference to yours, or it’s released and never called:
final class CheckoutViewController: UIViewController, PWFeatureFlagsUpdateListener { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) Pushwoosh.configure.featureFlags.addUpdateListener(self) }
override func viewWillDisappear(_ animated: Bool) { Pushwoosh.configure.featureFlags.removeUpdateListener(self) super.viewWillDisappear(animated) }
func featureFlagsDidUpdate() { render() }}To request the flags yourself, for example after an action that may move the user into a different segment, call refresh(completion:). While the stored flags are valid, it sends at most one request per 5 minutes and answers from the stored flags within that window. The completion runs on the main thread:
Pushwoosh.configure.featureFlags.refresh { error in if let error = error as NSError? { let status = error.code // the stored flags, or your defaults, stay in effect } render()}On failure, the error has the PWFeatureFlagsErrorDomain domain, and its code is the HTTP status. The code is 0 when the request got no HTTP response or wasn’t sent, and 200 when the server answered but the answer couldn’t be used.
To test refreshes without the 5-minute limit, set the Pushwoosh_FEATURE_FLAGS_DISABLE_THROTTLE Boolean key in Info.plist to YES.
If Pushwoosh can’t match the device’s segments in time, it returns a partial answer. The SDK doesn’t store it on the device, and the next refresh brings the full answer.
When a flag is killed in the Control Panel, devices get it off with default property values at the next refresh. When the SDK can’t reach the server, it keeps using the stored flags until they are 24 hours old, and your defaults after that.
Log impressions
Anchor link toAn impression tells Pushwoosh that a user actually reached the code a flag controls, so you can compare users who got the feature with those who didn’t. Call logImpression(_:) at the moment the user sees the feature or its old version, for both the enabled and the disabled branch, because users with the flag off are your comparison group:
let flags = Pushwoosh.configure.featureFlagslet isOn = flags.isEnabled("new_checkout", default: false)flags.logImpression("new_checkout")if isOn { showNewCheckout()} else { showOldCheckout()}The SDK posts the PW_FeatureFlagImpression event with these attributes:
| Attribute | Type | Value |
|---|---|---|
flag_key | string | The flag key. |
rule_id | string | The rule that decided the result, as in ruleId. |
enabled | boolean | Whether the flag was on. |
You don’t need to create the event in advance, because Pushwoosh adds PW_FeatureFlagImpression with these attributes to Audience → Events when the first impression arrives. The SDK sends one impression per flag, rule, and state in a session, and sends nothing when there is no flag for the key. If sending fails, the next logImpression call for the flag sends it again.
Impressions also let marketers build a segment of users who reached the feature: the saw the feature (30 days) state of the feature flag condition.
Method reference
Anchor link toPushwoosh.configure.featureFlags has these methods:
| Method | Description |
|---|---|
get(_:) | Returns the PWFeatureFlag for the key, or nil. Reads the stored flags only. |
isEnabled(_:default:) | Returns whether the flag is on, or the default when there is no flag. |
refresh(completion:) | Requests the flags, throttled to once per 5 minutes while the stored flags are valid. The completion gets nil or an error in PWFeatureFlagsErrorDomain, and can be nil. |
addUpdateListener(_:) | Calls the listener’s featureFlagsDidUpdate() after every refresh that changed a flag. The listener is held weakly. |
removeUpdateListener(_:) | Removes a listener added with addUpdateListener(_:). |
logImpression(_:) | Posts a PW_FeatureFlagImpression event for the flag’s current rule and state. |