Skip to main content

Flagsmith iOS SDK

This library can be used with iOS and Mac applications. The source code for the client is available on GitHub.

Installation

CocoaPods

Add the Flagsmith SDK as a dependency to your Podfile:

pod 'FlagsmithClient', '~> 3.5.0'
Swift Package Manager

Add the Flagsmith SDK as a dependency to your Package.swift file:

dependencies: [
    .package(url: "https://github.com/Flagsmith/flagsmith-ios-client.git", from: "3.8.1"),
]

Alternatively, you can add the Flagsmith SDK as a dependency from its repository URL using Xcode:

https://github.com/Flagsmith/flagsmith-ios-client.git

Basic Usage

The SDK is initialised against a single environment within a project on https://flagsmith.com, for example the Development or Production environment. You can find your Client-side Environment Key in the Environment settings page.

Initialization

Within your application delegate (usually AppDelegate.swift) add:

import FlagsmithClient
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

Flagsmith.shared.apiKey = "<YOUR_ENVIRONMENT_KEY>"
// The rest of your launch method code
}

Now you are all set to retrieve feature flags from your project. For example to list and print all flags:

Flagsmith.shared.getFeatureFlags() { (result) in
switch result {
case .success(let flags):
for flag in flags {
let name = flag.feature.name
let value = flag.value?.stringValue
let enabled = flag.enabled
print(name, "= enabled:", enabled, "value:", value ?? "nil")
}
case .failure(let error):
print(error)
}
}

Note that you can use:

  • flag.value?.stringValue
  • flag.value?.intValue

Based on your desired type.

To retrieve a feature flag boolean value by its name:

Flagsmith.shared.hasFeatureFlag(withID: "test_feature1", forIdentity: nil) { (result) in
print(result)
}

To retrieve a config value by its name:

Flagsmith.shared.getFeatureValue(withID: "test_feature2", forIdentity: nil) { (result) in
switch result {
case .success(let value):
print(value ?? "nil")
case .failure(let error):
print(error)
}
}

These methods can also specify a particular identity to retrieve the values for a user registration. See Identities , using the forIdentity parameter.

To retrieve a trait for a particular identity (see Traits):

Flagsmith.shared.getTraits(forIdentity: "test_user@test.com") {(result) in
switch result {
case .success(let traits):
for trait in traits {
let name = trait.key
let value = trait.value
print(name, "=", value)
}
case .failure(let error):
print(error)
}
}

To retrieve a flag for a particular identity:

Flagsmith.shared.getFeatureFlags(forIdentity: "test_user@test.com") {(result) in
switch result {
case .success(let flags):
for flag in flags {
let name = flag.feature.name
let value = flag.value?.stringValue
let enabled = flag.enabled
print(name, "= enabled:", enabled, "value:", value ?? "nil")
}
case .failure(let error):
print(error)
}
}

If you would prefer to do this using async/await you can do the following:

let flags = try await Flagsmith.shared.getFeatureFlags(forIdentity: "test_user@test.com")
for flag in flags {
let name = flag.feature.name
let value = flag.value?.stringValue
let enabled = flag.enabled
print(name, "= enabled:", enabled, "value:", value ?? "nil")
}

You can also retrieve flags for a particular identity and set traits at the same time:

Flagsmith.shared.getFeatureFlags(forIdentity: "test_user@test.com", traits: [Trait(key: "selected_tint_color", value: "orange")]) {(result) in
switch result {
case .success(let flags):
for flag in flags {
let name = flag.feature.name
let value = flag.value?.stringValue
let enabled = flag.enabled
print(name, "= enabled:", enabled, "value:", value ?? "nil")
}
case .failure(let error):
print(error)
}
}

If you would prefer to do this using async/await you can do the following:

let flags = try await Flagsmith.shared.getFeatureFlags(forIdentity: "test_user@test.com", traits: [Trait(key: "selected_tint_color", value: "orange")])
for flag in flags {
let name = flag.feature.name
let value = flag.value?.stringValue
let enabled = flag.enabled
print(name, "= enabled:", enabled, "value:", value ?? "nil")
}

Override Default Configuration

In AppDelegate.swift:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
Flagsmith.shared.apiKey = "<add your API key from the Flagsmith settings page>"

// set cache on / off (defaults to off)
Flagsmith.shared.cacheConfig.useCache = true

// set custom cache to use (defaults to shared URLCache)
//Flagsmith.shared.cacheConfig.cache = <CUSTOM_CACHE>

// set skip API on / off (defaults to off)
Flagsmith.shared.cacheConfig.skipAPI = false

// set cache TTL in seconds (defaults to 0, i.e. infinite)
Flagsmith.shared.cacheConfig.cacheTTL = 90

// set analytics on or off
Flagsmith.shared.enableAnalytics = true

// set the analytics flush period in seconds
Flagsmith.shared.analyticsFlushPeriod = 10

Flagsmith.shared.getFeatureFlags() { (result) in
print(result)
}
Flagsmith.shared.hasFeatureFlag(withID: "freeze_delinquent_accounts") { (result) in
print(result)
}
//Flagsmith.shared.setTrait(Trait(key: "<my_key>", value: "<my_value>"), forIdentity: "<my_identity>") { (result) in print(result) }
//Flagsmith.shared.getIdentity("<my_key>") { (result) in print(result) }
return true
}

Swift Concurrency

When running with Swift version 5.5.2 and greater (Xcode 13.2), async versions of the Flagsmith API become available. These are provided using the generic withCheckedThrowingContinuation(function:_:) Swift api, to wrap the closure based syntax. The async/await syntax provides a streamlined execution flow leading to greater code clarity. For example:

/// (Example) Setup the app based on the available feature flags.
func determineAppConfiguration() async throws {
let flagsmith = Flagsmith.shared

if try await flagsmith.hasFeatureFlag(withID: "ab_test_enabled") {
if let theme = try await flagsmith.getFeatureValue(withID: "app_theme") {
setTheme(theme)
} else {
let flags = try await flagsmith.getFeatureFlags()
processFlags(flags)
}
} else {
let trait = Trait(key: "selected_tint_color", value: "orange")
let identity = "4DDBFBCA-3B6E-4C59-B107-954F84FD7F6D"
try await flagsmith.setTrait(trait, forIdentity: identity)
}
}

Providing Default Flags

You can define default flag values when initialising the SDK. This ensures that your application works as intended in the event that it cannot receive a response from our API.

// set default flags
Flagsmith.shared.defaultFlags = [Flag(featureName: "feature_a", enabled: false),
Flag(featureName: "font_size", intValue:12, enabled: true),
Flag(featureName: "my_name", stringValue:"Testing", enabled: true)]

Cache

By default, the cache is off. When turned on, Flagsmith will cache all flags returned by the API (to permanent storage), and in case of a failed response, fall back on the cached values. The cache can be turned off or on using:

// set cache on / off (defaults to off)
Flagsmith.shared.cacheConfig.useCache = true

You can also set a TTL for the cache (in seconds), and request that Flagsmith skip calling the API if a valid cache is present

// set skip API on / off (defaults to off)
Flagsmith.shared.cacheConfig.skipAPI = false

// set cache TTL in seconds (defaults to 0, i.e. infinite)
Flagsmith.shared.cacheConfig.cacheTTL = 0

If more customisation is required, you can override the cache implemention with your own subclass of URLCache, using the following code.

// set custom cache to use (defaults to shared URLCache)
Flagsmith.shared.cacheConfig.cache = <CUSTOM_CACHE_IMPLEMENTATION>

Real Time Updates

By default real-time updates are disabled. When enabled the SDK will listen for changes to flags and update the cache as needed. If you want to enable real-time updates you can do so by setting the following flag:

Flagsmith.shared.enableRealTimeUpdates = false

It's possible to listen to updates in real time from the SDK using the flagStream property.

func subscribeToFlagUpdates() {
Task {
for await updatedFlags in flagsmith.flagStream {
DispatchQueue.main.async {
flags = updatedFlags
}
}
}
}

You can find an example of this functionality in the Flagsmith iOS Example app, which should work on your own data if you replace your Environment Key in the AppDelegate.swift file.

Override default configuration

By default, the client uses a default configuration. You can override the configuration as follows:

Override just the default API URI with your own:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

Flagsmith.shared.apiKey = "<YOUR_API_KEY>"
Flagsmith.shared.baseURL = "https://<your-self-hosted-api>/api/v1/"
Flagsmith.eventSourceBaseURL = "https://<your-self-hosted-api>/api/v1/"
// The rest of your launch method code
}