> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.getunleash.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.getunleash.io/_mcp/server.

# iOS SDK

> Set up the Unleash iOS SDK to evaluate feature flags in Swift apps with context, variants, bootstrap, events, and headers.

The Unleash iOS SDK is a Swift client that lets you evaluate feature flags in iOS and macOS applications. It connects to Unleash or [Unleash Edge](/unleash-edge) to fetch evaluated flags for a given [Unleash context](/concepts/unleash-context).

You can use this SDK with [Unleash Enterprise](https://www.getunleash.io/pricing) or [Unleash Open Source](https://github.com/Unleash/unleash).

For an overview of how Unleash SDKs work, including offline behavior, feature compatibility across SDKs, and default refresh and metrics intervals, refer to the [SDK overview](/sdks).

## Requirements

* MacOS: 12.15
* iOS: 12

## Installation

#### Swift Package Manager

In your `Package.swift` dependencies:

```swift
.package(url: "https://github.com/Unleash/unleash-ios-sdk.git", from: "0.0.0")
```

#### Xcode

**File > Add Package Dependencies**, enter `https://github.com/Unleash/unleash-ios-sdk`.

## Usage

To get started, import the SDK and initialize the Unleash client:

### iOS >= 13

```swift
import SwiftUI
import UnleashProxyClientSwift

// Setup Unleash in the context where it makes most sense

var unleash = UnleashProxyClientSwift.UnleashClient(
    unleashUrl: "https://<unleash-instance>/api/frontend",
    clientKey: "<client-side-api-token>",
    refreshInterval: 15,
    appName: "test",
    context: ["userId": "c3b155b0-5ebe-4a20-8386-e0cab160051e"]
)

unleash.start()
```

### iOS >= 12

```swift
import SwiftUI
import UnleashProxyClientSwift

// Setup Unleash in the context where it makes most sense

var unleash = UnleashProxyClientSwift.UnleashClientBase(
    unleashUrl: "https://<unleash-instance>/api/frontend",
    clientKey: "<client-side-api-token>",
    refreshInterval: 15,
    appName: "test",
    context: ["userId": "c3b155b0-5ebe-4a20-8386-e0cab160051e"]
)

unleash.start()
```

In the example above we import the UnleashProxyClientSwift and instantiate the client. You need to provide the following parameters:

* `unleashUrl`: The full URL to either the [Unleash Frontend API](/api#frontend-api) or an [Unleash Edge instance](/unleash-edge) \[String]
* `clientKey`: A [frontend API token](/concepts/api-tokens-and-client-keys#frontend-tokens) for authenticating with the Frontend API or Unleash Edge \[String]
* `refreshInterval`: The polling interval in seconds, set to `0` to only poll once and disable a periodic polling \[Int]
* `appName`: The application name identifier \[String]
* `context`: Initial Unleash context fields (like `userId`, `sessionId`, etc.), excluding `appName` and `environment` which are configured separately. \[String: String]

Calling `unleash.start()` makes the initial request to retrieve the feature flag configuration and starts the background polling interval (if `refreshInterval > 0`).

Until the client fetches the initial configuration (signaled by the `ready` event), checking a feature flag might return the default value (often `false`). To ensure the configuration is loaded before checking flags, subscribe to the ready event. See the [Events](#events) section for details.

Once the configuration is loaded, you can check if a feature flag is enabled:

```swift
if unleash.isEnabled(name: "ios") {
    // do something
} else {
   // do something else
}
```

You can also set up [variants](/concepts/feature-flag-variants):

```swift
var variant = unleash.getVariant(name: "ios")
if variant.enabled {
    // do something
} else {
   // do something else
}
```

### Configuration options

The Unleash SDK accepts the following initialization options:

| option                | required | default                        | description                                                                                                                                                                                                                                                           |   |
| --------------------- | -------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - |
| unleashUrl            | yes      | n/a                            | The Unleash Edge URL to connect to.                                                                                                                                                                                                                                   |   |
| clientKey             | yes      | n/a                            | The frontend token to use for authenticating with the Frontend API or Unleash Edge.                                                                                                                                                                                   |   |
| appName               | no       | unleash-swift-client           | The name of the application using this SDK. Sent with metrics to Unleash Edge and included in the Unleash context.                                                                                                                                                    |   |
| environment           | no       | default                        | The name of the environment. Sent with metrics to Unleash Edge and included in the Unleash context.                                                                                                                                                                   |   |
| refreshInterval       | no       | 15                             | How often (in seconds) the SDK checks for updated flag configurations. Set to 0 to disable polling after initial fetch.                                                                                                                                               |   |
| metricsInterval       | no       | 30                             | How often (in seconds) the SDK sends usage metrics back to Unleash Edge.                                                                                                                                                                                              |   |
| disableMetrics        | no       | false                          | Set this to `true` to disable usage metrics.                                                                                                                                                                                                                          |   |
| context               | no       | \[:]                           | The initial context parameters excluding `appName` and `environment` which are specified as top level fields.                                                                                                                                                         |   |
| poller                | no       | nil                            | A custom poller instance. If provided, the client ignores its own `refreshInterval`, `customHeaders`, `customHeadersProvider`, and `bootstrap` options. Use for advanced control or mocking.                                                                          |   |
| pollerSession         | no       | `URLSession.shared`            | Session object used for performing HTTP requests. You can provide a custom `PollerSession` for custom `URLSession` configuration or `URLRequest` interception.                                                                                                        |   |
| customHeaders         | no       | `[:]`                          | Additional headers to use when making HTTP requests to Unleash Edge. In case of name collisions with the default headers, the `customHeaders` value will be used.                                                                                                     |   |
| customHeadersProvider | no       | `DefaultCustomHeadersProvider` | Custom header provider for additional headers. In case of name collisions with the `customHeaders`, the `customHeadersProvider` value will be used.                                                                                                                   |   |
| bootstrap             | no       | empty list of feature flags    | Initial flag configurations provided to the Unleash client SDK. Can be a list of `Toggle` objects or the path to a JSON file matching the [Frontend API response format](/api/get-frontend-features#response). Available immediately on init, before the first fetch. |   |

### Bootstrap

You can bootstrap the SDK with flag configuration to evaluate flags before connecting to Unleash. Provide the bootstrap as a list of `Toggle` objects or from a JSON file matching the [Frontend API](/api#frontend-api) response format.

#### Inline data

```swift
let bootstrapList = Bootstrap
    .toggles(
        [
            Toggle(name: "Foo", enabled: true),
            Toggle(
                name: "Bar",
                enabled: false,
                variant: Variant(
                    name: "bar",
                    enabled: true,
                    featureEnabled: true,
                    payload: Payload(type: "string", value: "baz")
                )
            )
        ]
    )
```

#### JSON file

Create a JSON file matching the Frontend API response format:

```json
{
  "toggles": [
      {
        "name": "no-variant",
        "enabled": true
      },
      {
        "name": "enabled-with-variant-enabled-and-payload",
        "enabled": true,
        "variant": {
            "name": "bar",
            "enabled": true,
            "feature_enabled": true,
            "payload": {
                "type": "string",
                "value": "baz"
            }
        }
      }
  ]
}
```

Then load it in your app:

```swift
guard let filePath = Bundle.main.path(forResource: "FeatureResponseFile", ofType: "json") else {
    // Handle missing file
}

let bootstrapFile = Bootstrap.jsonFile(path: filePath)
```

You can inject the bootstrap at initialization or when calling `start`:

```swift
import SwiftUI
import UnleashProxyClientSwift

let unleash = UnleashClient(
    unleashUrl: "https://<unleash-instance>/api/frontend",
    clientKey: "<client-side-api-token>",
    bootstrap: .toggles([Toggle(name: "Foo", enabled: true)])
)

// Flags are available immediately
let isFooEnabled = unleash.isEnabled(name: "Foo") // true

// Or provide when starting
unleash.start(bootstrap: .jsonFile("path/to/json/file"))

// Or using async-await (iOS 13+)
await unleash.start(bootstrap: .jsonFile("path/to/json/file"))
```

* If you initialize the client with a `Poller`, inject the bootstrap directly into the poller. Any bootstrap data injected into the client options will be ignored when a custom poller is also provided.
* Bootstrapped flag configurations are replaced entirely after the first successful fetch.
* If bootstrap flags are provided when calling start, the first fetch occurs after the configured `refreshInterval` (default 15 seconds).
* Calling `updateContext(...)` before the first fetch removes any bootstrapped flags.

### Update context

To update the context, use the following method:

```swift
var context: [String: String] = [:]
context["userId"] = "c3b155b0-5ebe-4a20-8386-e0cab160051e"
unleash.updateContext(context: context)
```

This will stop and start the polling interval in order to renew polling with new context values.

You can use any of the [predefined fields](/concepts/unleash-context#structure). If you need to support
[custom properties](/concepts/unleash-context#the-properties-field) pass them as the second argument:

```swift
var context: [String: String] = [:]
context["userId"] = "c3b155b0-5ebe-4a20-8386-e0cab160051e"
var properties: [String: String] = [:]
properties["customKey"] = "customValue";
unleash.updateContext(context: context, properties: properties)
```

### Custom PollerSession

If you want to use a custom `URLSession` or intercept `URLRequest` you can provide a custom `PollerSession` to the client.

```swift
class CustomPollerSession: PollerSession {
    func perform(_ request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) {
        // Custom URLSession configuration
        let configuration = URLSessionConfiguration.default
        configuration.timeoutIntervalForRequest = 30

        // Modify URLRequest if needed
        var modifiedRequest = request
        modifiedRequest.setValue("foo", forHTTPHeaderField: "bar")

        let session = URLSession(configuration: configuration)
        session.dataTask(with: modifiedRequest, completionHandler: completionHandler).resume()
    }
}

// Use when initializing Unleash client
var unleash = UnleashProxyClientSwift.UnleashClient(
    unleashUrl: unleashUrl,
    clientKey: clientKey,
    pollerSession: CustomPollerSession()
)
```

### Custom HTTP headers

If you want the client to send custom HTTP headers with all requests to the Unleash API you can define that by setting them via the `UnleashClientBase`.

Custom and dynamic custom headers does not apply to sensitive headers.

* `Content-Type`
* `If-None-Match`
* anything starting with `unleash-` (`unleash-appname`, `unleash-connection-id`, `unleash-sdk`, ...)

```swift
var unleash = UnleashProxyClientSwift.UnleashClientBase(
    unleashUrl: unleashUrl,
    clientKey: clientKey,
    refreshInterval: 15,
    appName: "test",
    context: ["userId": "c3b155b0-5ebe-4a20-8386-e0cab160051e"],
    customHeaders: ["X-Custom-Header": "CustomValue", "X-Another-Header": "AnotherValue"]
)
```

### Dynamic custom HTTP headers

If you need custom HTTP headers that change during the lifetime of the client, a provider can be defined via the `UnleashClientBase`.

```swift
public class MyCustomHeadersProvider: CustomHeadersProvider {
    public init() {}
    public func getCustomHeaders() -> [String: String] {
        let token = "Acquire or refresh token";
        return ["Authorization": token]
    }
}
```

```swift
let myCustomHeadersProvider: CustomHeadersProvider = MyCustomHeadersProvider()

var unleash = UnleashProxyClientSwift.UnleashClientBase(
        unleashUrl: unleashUrl,
        clientKey: clientKey,
        refreshInterval: 15,
        appName: "test",
        context: ["userId": "c3b155b0-5ebe-4a20-8386-e0cab160051e"],
        customHeadersProvider: myCustomHeadersProvider
)
```

## Events

The client emits events that you can subscribe to using the `subscribe(name:callback:)` method or the `UnleashEvent` enum.

The client emits the following events:

* `ready` (`UnleashEvent.ready`): Emitted once the client has successfully fetched and cached the initial feature flag configurations.
* `update` (`UnleashEvent.update`): Emitted when a subsequent fetch results in a change to the feature flag configurations.
* `sent` (`UnleashEvent.sent`): Emitted when usage metrics have been successfully sent to the server.
* `error` (`UnleashEvent.error`): Emitted if an error occurs when trying to send metrics.
* `impression` (`UnleashEvent.impression`): Emitted when `isEnabled(name:)` or `getVariant(name:)` is called for a flag with [impression data](/concepts/impression-data) enabled.

Subscribe using the `subscribe(name:callback:)` method or the `UnleashEvent` enum:

```swift
func handleReady() {
    // do this when unleash is ready
}

unleash.subscribe(name: "ready", callback: handleReady)

// Or using the enum:
unleash.subscribe(.ready, callback: handleReady)
```

### Impression events

To track feature exposures, [enable impression data](/concepts/impression-data#enabling-impression-data) for the flags you want to track, then subscribe to the `impression` event:

```swift
import UnleashProxyClientSwift

func handleImpressionEvent(_ payload: Any?) {
    guard let impressionEvent = payload as? UnleashProxyClientSwift.ImpressionEvent else {
        return
    }

    // Send impression data to your analytics tool
}

unleash.subscribe(.impression, callback: handleImpressionEvent)
```

## Migrating to v2

In v2, the `StorageProvider` interface [was changed](https://github.com/Unleash/unleash-ios-sdk/pull/113) to accept all flags at once:

```swift
func set(values: [String: Toggle])
```

If you have a custom `StorageProvider` implementation, you'll need to update it.