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

# iOS SDK

> Integrate server-driven UI into your iOS and macOS apps with SwiftUI

The Metabind iOS SDK (`metabind-apple`) lets you render server-driven content in your Swift applications. Build dynamic experiences that update instantly without app store releases.

<Card title="View on GitHub" icon="github" href="https://github.com/metabindai/metabind-apple">
  `metabind-apple` — source code, issues, and releases.
</Card>

## Requirements

| Requirement | Version       |
| ----------- | ------------- |
| Xcode       | 15.0 or later |
| iOS         | 17.0 or later |
| macOS       | 14.0 or later |
| Swift       | 5.9 or later  |

## Installation

Add the Metabind SDK to your project using Swift Package Manager.

<Tabs>
  <Tab title="Xcode">
    1. Open your project in Xcode
    2. Go to **File → Add Package Dependencies**
    3. Enter the repository URL:

    ```
    https://github.com/metabindai/metabind-apple
    ```

    4. Select the version and add to your target
  </Tab>

  <Tab title="Package.swift">
    Add Metabind to your package dependencies:

    ```swift theme={null}
    dependencies: [
        .package(url: "https://github.com/metabindai/metabind-apple", from: "1.2.9")
    ]
    ```

    Then add it to your target:

    ```swift theme={null}
    .target(
        name: "YourApp",
        dependencies: ["Metabind"]
    )
    ```
  </Tab>
</Tabs>

## Quick start

### 1. Initialize the client

Create a `MetabindClient` with your API credentials from the [Metabind dashboard](https://metabind.ai):

```swift theme={null}
import SwiftUI
import Metabind

@main
struct MyApp: App {

    @State var client = MetabindClient(
        url: URL(string: "https://api.metabind.ai/graphql")!,
        ws: URL(string: "wss://ws-api.metabind.ai")!,
        apiKey: "your-api-key",
        organizationId: "your-org-id",
        projectId: "your-project-id"
    )

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .environment(client)
    }
}
```

### 2. Display content

Use `MetabindView` to render content by its ID:

```swift theme={null}
struct ContentView: View {

    var body: some View {
        MetabindView(contentId: "your-content-id")
    }
}
```

To receive live updates when the content changes in Metabind, opt in to the WebSocket subscription:

```swift theme={null}
MetabindView(contentId: "your-content-id", enableSubscription: true)
```

### 3. Handle actions

Listen for actions triggered by user interactions:

```swift theme={null}
MetabindView(contentId: "your-content-id")
    .onMetabindAction { action in
        switch action.name {
        case "metabind.content":
            // Navigate to another content page
            if let contentId = action.props["contentId"] as? String {
                navigateTo(contentId)
            }
        case "custom.action":
            // Handle your custom actions
            handleCustomAction(action.props)
        default:
            break
        }
    }
```

## Core concepts

### MetabindClient

The client manages the connection to Metabind's API and handles:

* **Authentication** — API key validation and request signing
* **Content fetching** — GraphQL queries for content and components
* **Real-time updates** — WebSocket subscriptions for live content changes
* **Caching** — a local SQLite-backed cache of fetched content and components

Inject the client into the SwiftUI environment so all views can access it:

```swift theme={null}
.environment(client)
```

For direct access, `MetabindClient` exposes `async` fetchers — `fetchContent(id:)`, `fetchContents(typeId:limit:)` — along with `streamContent(id:)` and `subscribeToContent(id:)` for cache-then-network and real-time streams.

### MetabindView

`MetabindView` is the primary way to display content. It automatically:

* Fetches the content and its required components
* Renders the UI based on your design system
* Subscribes to real-time updates when `enableSubscription` is `true`
* Handles loading and error states

```swift theme={null}
MetabindView(contentId: "content-id", enableSubscription: false)
```

### Actions

Actions are events triggered by user interactions with your content. Handle them with the `onMetabindAction` modifier, which delivers a `MetabindContentAction` (a `name` and a `props` dictionary):

| Action             | Description                      |
| ------------------ | -------------------------------- |
| `metabind.content` | Navigate to another content page |
| `metabind.url`     | Open an external URL             |
| `metabind.dismiss` | Dismiss the current view         |
| Custom actions     | Your own defined actions         |

## Real-time updates

For live content, either pass `enableSubscription: true` to `MetabindView` or subscribe directly. `subscribeToContent(id:)` opens a GraphQL WebSocket subscription and yields the content each time it changes in Metabind:

```swift theme={null}
for await result in client.subscribeToContent(id: "your-content-id") {
    if case .success(let content) = result {
        // Re-render or update state with the new content
    }
}
```

The client pauses the subscription when the app enters the background and resumes it on return. For cache-then-network delivery without a live subscription, use `streamContent(id:)`.

## Direct GraphQL access

Beyond `MetabindView`, `MetabindClient` exposes the underlying GraphQL operations directly, all following the same shape:

* **`fetch*`** — a single `async` request.
* **`stream*`** — an `AsyncStream` that yields cached data first, then network updates.
* **`subscribeTo*`** — a real-time WebSocket subscription (content only).

These cover content, components, content types, assets, tags, packages, and saved searches. For the full method catalog and parameters, see the [client API reference](https://github.com/metabindai/metabind-apple#graphql-apis); for the GraphQL API itself, see the [GraphQL docs](/graphql/overview).

## Navigation

For apps with multiple content pages, integrate with SwiftUI's navigation system:

```swift theme={null}
struct ContentView: View {

    @State private var path = NavigationPath()

    private enum Destination: Hashable {
        case content(id: String)
    }

    var body: some View {
        NavigationStack(path: $path) {
            MetabindView(contentId: "home")
                .onMetabindAction { action in
                    if action.name == "metabind.content",
                       let contentId = action.props["contentId"] as? String
                    {
                        path.append(Destination.content(id: contentId))
                    }
                }
                .navigationDestination(for: Destination.self) { destination in
                    switch destination {
                    case .content(let id):
                        MetabindView(contentId: id)
                    }
                }
        }
    }
}
```

## Platform support

| Platform | Minimum Version |
| -------- | --------------- |
| iOS      | 17.0            |
| macOS    | 14.0            |

## Next steps

<CardGroup cols={2}>
  <Card title="Sample app" icon="mobile" href="/samples/retail-app-ios">
    See a complete example of the SDK in action.
  </Card>

  <Card title="Build components" icon="puzzle-piece" href="/guides/building/authoring-components">
    Create custom components for your app.
  </Card>

  <Card title="BindJS reference" icon="code" href="/bindjs/introduction">
    Learn the component authoring language.
  </Card>

  <Card title="Managing content" icon="file-lines" href="/content/overview">
    Understand how to structure content.
  </Card>
</CardGroup>
