Swift On Device Evaluation SDK
Statsig's Swift SDK for on-device evaluation with iOS, macOS, tvOS, and watchOS.
Pros
- No network request is needed when changing user properties: check the gate/config/experiment locally
- You can bring your own CDN or synchronously initialize with a preloaded project definition
- Lower latency to download configs cached at the edge, rather than evaluated for a given user (which can't be cached as much)
Cons
- The entire project definition is available client-side: the names and configurations of all experiments and feature flags accessible by your client key are exposed. Refer to client key with server permission best practices.
- Payload size is larger than what is required for the traditional SDKs
- Evaluation performance is slightly slower: rather than looking up the value, the SDK must evaluate targeting conditions and an allocation decision
- Does not support ID list segments with > 1000 IDs
- Does not support IP or User Agent based checks (Browser Version/Name, OS Version/Name, IP, Country)
Set up the SDK
Install the SDK
To use the SDK in your project, you must add Statsig as a dependency.
// In your Xcode, select File > Swift Packages > Add Package Dependency // and enter the URL https://github.com/statsig-io/swift-on-device-evaluations-sdk.git. // // You can also include it directly in your project's Package.swift. // Find out the latest release version on our GitHub page: // https://github.com/statsig-io/swift-on-device-evaluations-sdk/releases dependencies: [ // see the latest version on https://github.com/statsig-io/swift-on-device-evaluations-sdk/releases .package(url: "https://github.com/statsig-io/swift-on-device-evaluations-sdk.git", .upToNextMinor("X.Y.Z")), ], //... targets: [ .target( name: "YOUR_TARGET", dependencies: ["StatsigOnDeviceEvaluations"] ) ],Initialize the SDK
Next, initialize the SDK with a client SDK key from the "API Keys" tab on the Statsig console. These keys are safe to embed in a client application.Along with the key, pass in a User Object with the attributes you want to target later on in a gate or experiment.For On-Device Evaluation, add the "Allow Download Config Specs" scope. Client keys, by default, can't download the project definition for on-device evaluation.
While client keys are safe to include, Server and Console keys should always be kept private.
import StatsigOnDeviceEvaluations // (optional) Configure the SDK if needed let opts = StatsigOptions() opts.environment.tier = "staging" Statsig.shared.initialize("client-sdk-key", options: opts) { err in if let err = err { print("Error \(err)") } } // or, create your own instance let myStatsigInstance = Statsig() myStatsigInstance.initialize("client-sdk-key", options: opts) { err in if let err = err { print("Error \(err)") } }You can configure the SDK to use cached values if they are newer than the local file. This is useful when you ship your app with a local file but want it used only for the first session. In the following example, the SDK uses
initialSpecsonly when there is no cache or the cache is older thaninitialSpecs.swiftlet options = StatsigOptions() options.useNewerCacheValuesOverProvidedValues = true client.initializeSync( "client-sdk-key", initialSpecs: specs, options: options )You can get a copy of your current specs data by visiting:
https://api.statsigcdn.com/v1/download_config_specs/client-{YOUR_SDK_KEY}.json
Working with the SDK
Checking a feature flag/gate
After the SDK is initialized, check a Feature Gate. Feature Gates create logic branches in code that can be rolled out to different users from the Statsig Console. Gates are always CLOSED or OFF (thinkreturn false;) by default.// Simple Pass/Fail check
let isPassing: Bool = Statsig.shared.checkGate("my_gate", user)
// or, the verbose FeatureGate check
let gate = Statsig.shared.getFeatureGate("my_gate", user)
print(gate.evaluationDetails.reason) // "Network" | "Cache" | "Unrecognized"
let isPassing: Bool = gate.value
Reading a dynamic config
Feature Gates are useful for simple on/off switches with optional advanced user targeting. If you need to send a different set of values (strings, numbers, and so on) to your clients based on specific user attributes (such as country), use Dynamic Configs. The API is similar to Feature Gates, but you receive an entire JSON object you can configure on the server and fetch typed parameters from.
let config = Statsig.shared.getDynamicConfig("my_dynamic_config", user)
let name: String? = config.value["product_name"] as? String
let price: Double? = config.value["price"] as? Double
Getting a layer/experiment
Use Layers/Experiments to run A/B/n experiments. Two APIs are available, but layers are recommended because they enable quicker iterations with parameter reuse.// Getting values via getLayer
let layer = Statsig.shared.getLayer("my_layer", user)
let name: String? = layer.getValue(param: "product_name", fallback: "Unknown") as? String
// or, using getExperiment
let experiment = Statsig.shared.getExperiment("my_experiment", user)
let name: String? = experiment.value["product_name"] as? String
let price: Double? = experiment.value["price"] as? Double
Logging an event
After you set up a Feature Gate or an Experiment, you can track custom events to see how your features or experiment groups affect those events. Call the Log Event API for the event, and optionally provide a value and an object of metadata to be logged with the event:
let event = StatsigEvent(
eventName: "add_to_cart",
value: "SKU_1234",
metadata: [
"price": "9.99",
"item_name": "CoolProduct"
]
)
Statsig.shared.logEvent(event, user)
Code examples
Working sample apps are available in the repository:
Statsig user
Provide a StatsigUser object to check or get your configurations. Pass as much information as possible to use advanced gate and config conditions.
userID field is usually required to provide a consistent experience for a given user. Refer to logged-out experiments to understand how to correctly run experiments for logged-out users.In addition to userID, StatsigUser has the following top-level fields: email, ip, userAgent, country, locale, and appVersion. You can also pass any key-value pairs in an object or dictionary to the custom field to create targeting based on them.
After the user logs in or their attributes change, call updateUser with the updated userID and any other updated user attributes:
let user = StatsigUser(
userID: "a-user",
customIDs: ["EmployeeID": "an-employee"],
email: "user@statsig.io",
ip: "58.84.239.246",
userAgent: "Mozilla/5.0 (iPad; CPU OS 13_4_1....",
country: "NZ",
locale: "en_NZ",
appVersion: "3.2.1",
custom: ["Level": "9001"],
privateAttributes: ["SensitiveInfo": "shhh"]
)
Private attributes
The StatsigUser object has a privateAttributes field: an object/dictionary for setting private user attributes. Any attribute set in privateAttributes is used only for evaluation and targeting, and Statsig removes it from any logs before sending them to the server.
For example, if you have feature gates that should pass only for users with emails ending in "@statsig.com" but don't want to log users' email addresses to Statsig, add the key-value pair { email: "my_user@statsig.com" } to privateAttributes on the user.
Setting a global user
To avoid passing the user object to every evaluation call, you can set a global user. The global user is used for all evaluations unless otherwise specified.
Statsig.shared.setGlobalUser(myGlobalUser)
Statsig.shared.checkGate("my_gate") // <- Will use myGlobalUser
Statsig.shared.checkGate("my_gate", StatsigUser(userID: "user-123")) // <- Will NOT use myGlobalUser
Unlike precomputed evaluation SDKs, the on-device evaluation SDK doesn't have an updateUser method since it evaluates gates/configs/experiments in real-time for any user object you pass in.
Statsig options
Configure the SDK behavior by passing a StatsigOptions object during initialization.
eventQueueMaxSizeIntThe maximum number of events to batch before flushing logs to the server.
eventQueueInternalMsDoubleHow frequently to flush queued logs.
eventLoggingAPIStringThe API where all events are sent.
configSpecAPIStringThe API used to fetch the latest configurations.
environmentStatsigEnvironmentAn object you can use to set environment variables that apply to all of your users in the same session and Statsig uses for targeting purposes.
Lifecycle and advanced usage
Shutting Statsig down
The SDK keeps event logs in a client cache and flushes them periodically to save battery usage and prevent dropped events. Some events may not be sent when your app shuts down before a flush occurs.
To flush or save all logged events locally, call shutdown when your app is closing:
Statsig.shared.shutdown { err in
if let err = err {
print("An error occurred during Statsig shutdown: \(err)")
} else {
print("Statsig shutdown successfully")
}
}
Post-init syncing
From network
By default, the SDK syncs only during initialization. To re-sync after initialization, call the Statsig.update method. This triggers a network call to fetch the latest changes from the server.
Statsig.shared.update { err in
if let err = err {
print("Statsig update error: \(err)")
}
}
From a local file
If you maintain your own copy of the "specs" json, pass it to Statsig.updateSync(). This skips the network call and uses the provided specs instead.
let result = Statsig.shared.updateSync(updatedSpecs: myJsonData)
Scheduled polling
To have the SDK regularly poll for updates, start the polling task with Statsig.scheduleBackgroundUpdates(). This calls Statsig.update internally to fetch the latest changes from the network.
let pollingTask = Statsig.shared.scheduleBackgroundUpdates() // Defaults to 1 hour interval
// or, specify a custom interval
let pollingTask = Statsig.shared.scheduleBackgroundUpdates(intervalSeconds: 300)
// and, if you need to cancel it later
pollingTask?.cancel()
Local overrides
Override the values returned by the Statsig SDK for unit testing or for enabling features in local development.
To set up local overrides, pass an instance of LocalOverrideAdapter to the SDK through the StatsigOptions object.
OverrideAdapter protocol and passing it in instead.let user = StatsigUser(userID: "a-user")
let overrides = LocalOverrideAdapter()
// Override a gate
overrides.setGate(user, FeatureGate.create("local_override_gate", true))
// Override a dynamic config (Similar for Layer and Experiment)
overrides.setDynamicConfig(user, DynamicConfig.create("local_override_dynamic_config", ["foo": "bar"]))
let opts = StatsigOptions()
opts.overrideAdapter = overrides
Statsig.shared.initialize(YOUR_SDK_KEY, options: opts) { _ in
let gate = Statsig.shared.getFeatureGate("local_override_gate", user)
print("Result: \(gate.value) (\(gate.evaluationDetails.reason))")
}
FAQs
Additional resources
Was this helpful?