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

# iOS Swift SDK

> Add native Woes live chat to a SwiftUI or UIKit iOS app.

# iOS Swift SDK

The Woes iOS SDK gives your customers a native live-chat experience in SwiftUI or UIKit. It uses the same public widget API as the web widget, so messages still route to the configured workspace or agent widget key and appear in the Woes Inbox.

## What The SDK Does

| Capability              | Details                                                                        |
| ----------------------- | ------------------------------------------------------------------------------ |
| Native UI               | Ships a SwiftUI `WoesLiveChatView` and UIKit `WoesLiveChatViewController`.     |
| Widget routing          | Uses a workspace key (`trovw_...`) or agent key (`trovw_agent_...`).           |
| Conversation continuity | Stores the conversation key and history secret per widget key.                 |
| AI and handoff          | Sends messages through Woes so the selected AI agent or operator can reply.    |
| Identity verification   | Attaches server-generated HMAC email hashes or JWT proofs to message requests. |
| Surveys                 | Submits configured CSAT or NPS surveys through the public widget API.          |

## Requirements

* iOS 15 or later.
* Swift 5.9 or later.
* A Woes workspace or agent widget public key from **Settings → Keys**.

## Install With Swift Package Manager

In Xcode, open **File → Add Package Dependencies...** and add the repository that contains Woes. Select the `WoesLiveChat` package under `sdk/ios`.

For local testing, add the local package path:

```text theme={"dark"}
/path/to/woes/sdk/ios
```

## SwiftUI Quick Start

```swift theme={"dark"}
import SwiftUI
import WoesLiveChat

struct SupportScreen: View {
    var body: some View {
        WoesLiveChatView(
            configuration: WoesConfiguration(
                publicKey: "trovw_agent_xxx",
                title: "Acme Support",
                identity: WoesIdentity(
                    email: "ada@example.com",
                    name: "Ada Lovelace",
                    emailHash: "<server-generated-hmac>"
                ),
                customFields: [
                    "plan": .string("pro"),
                    "build": .number(42)
                ]
            )
        )
    }
}
```

Mount the view wherever you want the support experience to appear. The SDK loads widget configuration, restores any previous conversation for that public key, sends messages, and polls for AI or operator replies.

## UIKit Quick Start

```swift theme={"dark"}
import UIKit
import WoesLiveChat

let controller = WoesLiveChatViewController(
    configuration: WoesConfiguration(
        publicKey: "trovw_agent_xxx",
        title: "Acme Support"
    )
)

present(controller, animated: true)
```

## Identity Verification

If your workspace requires verified widget identity, generate the proof on your server and pass it to the SDK:

```swift theme={"dark"}
WoesIdentity(
    email: "ada@example.com",
    name: "Ada Lovelace",
    emailHash: "<server-generated-hmac>"
)
```

Do not compute HMACs or store widget identity secrets in your iOS app. Use your backend to generate either:

* `emailHash` for HMAC email mode.
* `jwt` for JWT identity mode.

A mobile identity endpoint should use the same server-side contract as the web widget: authenticate the app user, normalize email with `trim().lowercased()`, sign with the server-only `WOES_WIDGET_IDENTITY_SECRET`, and return a no-store response. If the user signs in or switches accounts, refresh the SDK identity and send the new proof before the next message.

The current iOS SDK attaches identity proof to widget message requests. It does
not call the web widget's `/api/widget/identity` preflight route on its own.

If identity verification is disabled, `email`, `name`, and `company` can still help operators recognize the customer, but verified identity is required for trusted account-specific context.

## Custom Fields

Use `customFields` for safe, operator-visible context that should be attached to the next widget message:

```swift theme={"dark"}
WoesConfiguration(
    publicKey: "trovw_agent_xxx",
    customFields: [
        "plan": .string("enterprise"),
        "trial": .bool(false)
    ]
)
```

The widget API sanitizes these fields server-side and drops secret-like keys.

## Headless Mode

Use `WoesLiveChatViewModel` or `WoesAPIClient` if you want to build a custom interface:

```swift theme={"dark"}
let configuration = WoesConfiguration(publicKey: "trovw_agent_xxx")
let viewModel = WoesLiveChatViewModel(configuration: configuration)

await viewModel.start()
await viewModel.send("How do I create an API key?")
```

## API Boundaries

The SDK uses public widget routes, not the `/api/v1` REST API:

* `GET /api/widget/config`
* `GET /api/widget/messages`
* `POST /api/widget/messages`
* `POST /api/widget/presence`
* `GET /api/widget/feedback`
* `POST /api/widget/csat`

The current SDK does not implement widget telemetry events, published updates,
or update read-state routes. The SDK never uses REST API keys, Supabase
credentials, workspace secrets, operator debug traces, or provider/model
internals. Conversation history still depends on the per-widget-key
`conversationSecret`.

## Related Pages

* [Widget Routes](/api/widget-routes)
* [Widget Identity](/security/widget-identity)
* [Widget Runtime](/api/widget)
