---
title: Swift On Device Evaluation SDK
description: "Statsig's Swift SDK for on-device evaluation with iOS, macOS, tvOS, and watchOS."
product: general
token_estimate: 4559
---
# Swift On Device Evaluation SDK

> For AI agents: a documentation index is available at [/llms.txt](/llms.txt). Append `.md` to any page URL for markdown, or send `Accept: text/markdown`.

- **Package:** `StatsigOnDeviceEvaluations` ([cocoapods](https://cocoapods.org/pods/StatsigOnDeviceEvaluations))
- **Latest version:** 1.6.0
- **Repository:** [swift-on-device-evaluations-sdk](https://github.com/statsig-io/swift-on-device-evaluations-sdk)

> **Info:**
>
> Statsig recommends its normal (remote evaluation) SDKs for most client applications. Understand the use case and privacy risks by reading the [On-Device Eval SDK overview](https://docs.statsig.com/client/onDeviceOverview). On-device evaluation SDKs are for Enterprise & Pro Tier only.

These SDKs use a different paradigm than their precomputed counterparts: [JS](https://docs.statsig.com/client/javascript-sdk), [Android](https://docs.statsig.com/client/Android), [iOS](https://docs.statsig.com/client/iosClientSDK). On-device evaluation SDKs behave more like Server SDKs. Rather than requiring a user in advance, you can check gates, configs, and experiments for any set of user properties. 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 Statsig can't cache as much)

### Cons

- The client has the entire project definition: it exposes the names and configurations of all experiments and feature flags accessible by your client key. Refer to [client key with server permission best practices](https://docs.statsig.com/access-management/api-keys#client-keys-with-server-permissions).
- Payload size is larger than what the traditional SDKs require
- Evaluation performance is slightly slower: rather than looking up the value, the SDK must evaluate targeting conditions and an allocation decision
- Doesn't support ID list segments with > 1000 IDs
- Doesn't 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.

   #### Swift Package Manager

   ```swift
   // 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"]
       )
   ],
   ```

   #### CocoaPods

   ```ruby
   # If you are using CocoaPods, our pod name is 'StatsigOnDeviceEvaluations', 
   # and you can include the following line to your Podfile:

   use_frameworks!
   target 'TargetName' do
     //...
     pod 'StatsigOnDeviceEvaluations', '~> X.Y.Z'
   end

   # Find the latest versions by searching cocoapods.org or on Github:
   # https://github.com/statsig-io/swift-on-device-evaluations-sdk/releases
   ```
2. **Initialize the SDK**

   Next, initialize the SDK with a client SDK key from the ["API Keys" tab on the Statsig console](https://console.statsig.com/api_keys). These keys are safe to embed in a client application.

   Along with the key, pass in a [User Object](#statsig-user) with the attributes you want to target later on in a gate or experiment.

   > **Warning:**
   >
   > 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, always keep Server and Console keys private.

   #### How to add the scope

   #### New SDK Keys

   When creating a new client key, select **"Allow Download Config Specs"**

   ![Add DCS Scope to New Key](https://docs.statsig.com/images/local-eval/new-keys.png)

   #### Existing SDK Keys

   To add the scope to an existing key, under **Project Settings** → **API Keys** → **Client API Keys**, select **Actions** → **Edit Scopes**, and select **"Allow Download Config Specs"**, then **Save**.

   ![Add DCS Scope to Existing Key](https://docs.statsig.com/images/local-eval/existing-keys.png)

   #### Async (Swift)

   ```swift
   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)")
       }
   }
   ```

   #### Objective C

   ```objective-c
   StatsigOptions *options = [StatsigOptions new];

   StatsigEnvironment *env = [StatsigEnvironment new];
   env.tier = @"staging";
   options.environment = env;

   [[Statsig sharedInstance] 
     initializeWithSDKKey:@"client-sdk-key"
     options:options
     completion:^(NSError * _Nullable error) {
       if (error != nil) {
           NSLog(@"Error %@", error);
       }
   }];
   ```

   #### Synchronous (Swift)

   ```swift
   import StatsigOnDeviceEvaluations

   // (optional) Configure the SDK if needed
   let opts = StatsigOptions()
   opts.environment.tier = "staging"

   let specs: NSString = "..." // JSON string of your configurations

   let error = client.initializeSync("client-sdk-key", initialSpecs: specs)
   if let err = error {
       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
   )
   ```

   > **Note:**
   >
   > 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 you initialize the SDK, check a [**Feature Gate**](https://docs.statsig.com/feature-flags/overview). Feature Gates create logic branches in code that you can roll out to different users from the Statsig Console. Gates are always **CLOSED** or **OFF** (think `return false;`) by default.

#### Swift

```swift
// 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
```

#### Objective C

```objective-c
BOOL isPassing = [[Statsig sharedInstance] checkGate:@"my_gate" forUser:user];
```

### 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. Statsig offers two APIs but recommends [layers](https://docs.statsig.com/experiments/layers-overview) 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 log 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

Find working sample apps in the repository:

- [Swift & Objective C Examples](https://github.com/statsig-io/swift-on-device-evaluations-sdk/tree/main/Sample/App/Examples/OnDeviceEvaluations)

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

You usually need the `userID` field to provide a consistent experience for a given user. Refer to [logged-out experiments](https://docs.statsig.com/guides/first-device-level-experiment) 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:

#### Swift

```swift
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"]
)
```

#### Objective C

```objective-c
StatsigUser *user = [StatsigUser userWithUserID:@"a-user"];
user.customIDs = @{ @"EmployeeID": @"an-employee" };
user.email = @"user@statsig.io";
user.ip = @"58.84.239.246";
user.userAgent = @"Mozilla/5.0 (iPad; CPU OS 13_4_1....";
user.country = @"NZ";
user.locale = @"en_NZ";
user.appVersion = @"3.2.1";
[user.custom setString:@"9001" forKey:@"Level"];
[user.privateAttributes setString:@"shhh" forKey:@"SensitiveInfo"];
```

### Private attributes

The `StatsigUser` object has a `privateAttributes` field: an object/dictionary for setting private user attributes. Statsig uses any attribute set in `privateAttributes` only for evaluation and targeting, and removes it from any logs before sending them to the server.

For example, you might have feature gates that should pass only for users with emails ending in "@statsig.com", but you don't want to log users' email addresses to Statsig. In this case, 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 SDK uses the global user 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
```

> **Note:**
>
> 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.

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| eventQueueMaxSize | Int | No | — | The maximum number of events to batch before flushing logs to the server. |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| eventQueueInternalMs | Double | No | — | How frequently to flush queued logs. |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| eventLoggingAPI | String | No | — | The API where Statsig sends all events. |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| configSpecAPI | String | No | — | The API the SDK uses to fetch the latest configurations. |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| environment | StatsigEnvironment | No | — | 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. Statsig may not send some events when your app shuts down before a flush occurs.

To flush or save all logged events locally, call shutdown when your app is closing:

#### Swift

```swift
Statsig.shared.shutdown { err in
    if let err = err {
        print("An error occurred during Statsig shutdown: \(err)")
    } else {
        print("Statsig shutdown successfully")
    }
}
```

#### Objective C

```objective-c
[[Statsig sharedInstance] shutdownWithCompletion:^(NSError * _Nullable error) {
    if (error != nil) {
        NSLog(@"An error occurred during Statsig shutdown: %@", error);
    } else {
        NSLog(@"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 the Statsig SDK returns 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.

> **Note:**
>
> You can write your own override adapter by implementing the [`OverrideAdapter`](https://github.com/statsig-io/swift-on-device-evaluations-sdk/blob/main/Sources/StatsigOnDeviceEvaluations/OverrideAdapter.swift) protocol and passing it in instead.

#### Swift

```swift
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))")
}
```

#### Objective C

```objective-c
StatsigUser *user = [StatsigUser userWithUserID:@"a-user"];

LocalOverrideAdapter *overrides = [LocalOverrideAdapter new];

// Override a gate
[overrides
    setGateForUser:user
    gate:[FeatureGate createWithName:@"local_override_gate" andValue:true]];

// Override a dynamic config (Similar for Layer and Experiment)
[_overrides
    setDynamicConfigForUser:user
    config:[DynamicConfig
            createWithName:@"local_override_dynamic_config"
            andValue:@{@"foo": @"bar"}]];

StatsigOptions *options = [StatsigOptions new];
options.overrideAdapter = overrides;

[[Statsig sharedInstance] 
    initializeWithSDKKey:YOUR_SDK_KEY
    options:options
    completion:^(NSError * _Nullable error) {

    FeatureGate *gate = [[Statsig sharedInstance] getFeatureGate:@"local_override_gate" forUser:user options:nil];
    NSLog(@"Result: %d (%@)", gate.value, gate.evaluationDetails.reason);

}];
```

## FAQs

#### How do I run experiments for logged out users?

Refer to the guide on [device level experiments](https://docs.statsig.com/guides/first-device-level-experiment).

## Additional resources

- [On-Device Evaluation SDK Overview](https://docs.statsig.com/client/onDeviceOverview)
- [Client Keys with Server Permissions](https://docs.statsig.com/access-management/api-keys#client-keys-with-server-permissions)
- [Debugging SDK Evaluations](https://docs.statsig.com/sdks/debugging)

