> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gatlio.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Swift (iOS / macOS)

> Native billing enforcement for SwiftUI apps — supports iOS 15+ and macOS 13+.

## Installation

### Swift Package Manager

In Xcode: **File → Add Packages**, then enter:

```
https://github.com/gatlio/gatlio-swift
```

Or in `Package.swift`:

```swift theme={null}
.package(url: "https://github.com/gatlio/gatlio-swift.git", from: "1.0.0")
```

Add `GatlioUI` as a target dependency (and optionally `Gatlio` for direct client usage):

```swift theme={null}
.target(
    name: "YourApp",
    dependencies: [
        .product(name: "GatlioUI", package: "gatlio-swift"),
    ]
)
```

## Quick start

Wrap the authenticated portion of your app in `GatlioGate`:

```swift theme={null}
import GatlioUI

GatlioGate(
    tenantSlug: "acme",
    customerId: currentUser.stripeCustomerId,
    publishableKey: "pk_live_abc123",
    apiBase: "https://api.gatlio.io"
) {
    YourApp()
}
```

* **Active** — `YourApp` renders normally
* **Warning** — dismissable banner above content
* **Lockout** — full-screen overlay until card is updated

## Parameters

| Parameter        | Type                                                         | Required | Default  |
| ---------------- | ------------------------------------------------------------ | -------- | -------- |
| `tenantSlug`     | `String`                                                     | ✓        | —        |
| `customerId`     | `String`                                                     | ✓        | —        |
| `publishableKey` | `String`                                                     | ✓        | —        |
| `apiBase`        | `String`                                                     | ✓        | —        |
| `pollInterval`   | `TimeInterval`                                               |          | `600`    |
| `forcedStatus`   | `GatlioStatus?`                                              |          | `nil`    |
| `callbacks`      | `GatlioCallbacks?`                                           |          | `nil`    |
| `lockoutScreen`  | `((@escaping () -> Void, Entitlements?) -> AnyView)?`        |          | built-in |
| `warningBanner`  | `((@escaping () -> Void, @escaping () -> Void) -> AnyView)?` |          | built-in |
| `content`        | `@ViewBuilder`                                               | ✓        | —        |

## Callbacks

```swift theme={null}
GatlioGate(
    tenantSlug: "acme",
    customerId: currentUser.stripeCustomerId,
    publishableKey: "pk_live_abc123",
    apiBase: "https://api.gatlio.io",
    callbacks: GatlioCallbacks(
        onLockout: { print("locked out") },
        onWarning: { print("warning") },
        onActive: { print("active") },
        onRecovered: { print("recovered") },
        onError: { err in print("error: \(err)") }
    )
) {
    YourApp()
}
```

Callbacks fire on status **transitions only**.

## Custom enforcement UI

Wrap your views in `AnyView(...)`:

```swift theme={null}
GatlioGate(
    // ...
    lockoutScreen: { triggerCardUpdate, _ in
        AnyView(MyBrandedLockout(onUpdate: triggerCardUpdate))
    },
    warningBanner: { triggerCardUpdate, dismissWarning in
        AnyView(MyBrandedBanner(onUpdate: triggerCardUpdate, onDismiss: dismissWarning))
    }
) {
    YourApp()
}
```

## Testing

### Force a state

```swift theme={null}
GatlioGate(
    tenantSlug: "acme",
    customerId: "cus_test",
    publishableKey: "pk_test_abc",
    apiBase: "https://api.gatlio.io",
    forcedStatus: .lockout
) {
    YourApp()
}
```

No network calls are made when `forcedStatus` is set. Remove before shipping.

### Interactive sandbox

```swift theme={null}
import GatlioUI

GatlioSandbox(
    onLockout: { print("locked out") },
    onWarning: { print("warning") },
    onActive: { print("active") },
    onError: { err in print("error: \(err)") }
) {
    YourApp()
}
```

A `DEV` badge appears in the bottom-right corner. Tap to open a sheet with state pills and a callback log. Remove before shipping to production.

## UIKit / non-SwiftUI usage

Use the `Gatlio` target directly with Combine:

```swift theme={null}
import Gatlio

let client = GatlioClient(
    config: GatlioConfig(
        apiBase: "https://api.gatlio.io",
        tenantSlug: "acme",
        customerId: currentUser.stripeCustomerId,
        publishableKey: "pk_live_abc123"
    )
)
client.start()

client.$status
    .receive(on: DispatchQueue.main)
    .sink { status in /* update UI */ }
    .store(in: &cancellables)
```

Call `client.stop()` in `deinit` or `viewDidDisappear`.
