On this page

Swift On Device Evaluation SDK

Statsig's Swift SDK for on-device evaluation with iOS, macOS, tvOS, and watchOS.

Statsig's normal (remote evaluation) SDKs are recommended for most client applications. Understand the use case and privacy risks by reading the On-Device Eval SDK overview. On-device evaluation SDKs are for Enterprise & Pro Tier only.
These SDKs use a different paradigm than their precomputed counterparts: JS, Android, iOS. On-device evaluation SDKs behave more like Server SDKs. Rather than requiring a user up front, you can check gates, configs, and experiments for any set of user properties, because the SDK downloads a complete representation of your project and evaluates checks in real time.

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

  1. 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"]
        )
    ],
    
  2. 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 initialSpecs only when there is no cache or the cache is older than initialSpecs.

    swift
    let 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 (think return 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.

swift
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.
swift
// 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:

swift
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.

The 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.

swift
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.

eventQueueMaxSizeInt

The maximum number of events to batch before flushing logs to the server.

eventQueueInternalMsDouble

How frequently to flush queued logs.

eventLoggingAPIString

The API where all events are sent.

configSpecAPIString

The API used to fetch the latest configurations.

environmentStatsigEnvironment

An 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.

swift
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.

swift
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.

swift
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.

You can write your own override adapter by implementing the 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?