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

# 展示第一个广告

> 创建会话、喂入消息、挂载 InlineAdUIView 并订阅事件。

这是 5 分钟接入的完整流程。开始之前，请确认你已经完成 [安装](/sdk/swift/installation)。

## 1. 创建会话

入口是 `KontextAds.createSession(_:)`，它返回一个 `Session`——后续整个对话生命周期都通过它来操作。

```swift theme={null}
import KontextSwiftSDK

let session = KontextAds.createSession(SessionOptions(
    publisherToken: "<your-publisher-token>",
    userId: "user-1234",
    conversationId: "conv-5678",
    character: Character(
        id: "character-1234",
        name: "John Doe",
        avatarUrl: URL(string: "https://example.com/avatar.png")!,
        greeting: "Hello, how can I help you today?"
    ),
    advertisingId: nil, // 可选——你自行收集的 IDFA（为 nil 时 SDK 会自动收集）
    vendorId: nil, // 可选——你自行收集的 IDFV（为 nil 时 SDK 会自动收集）
    onEvent: { event in
        // 处理广告生命周期事件
        print("[kontext] \(event.name)")
    }
))
```

会话在其生命周期内会固定 `userId`、`conversationId` 和 `publisherToken`。当这些值变化时（例如用户开始新对话），请重建会话。

<Note>
  `Session` 是 `@MainActor` 隔离的——请在 main actor 上调用其方法（SwiftUI 视图和 `@MainActor` 隔离的 view controller 默认满足）。
</Note>

## 2. 喂入对话消息

每条消息出现时都要 add 到会话中。用户消息会在后台触发一次防抖后的预加载；助手消息让 SDK 把匹配到的广告绑定到对应的广告位上。

```swift theme={null}
session.addMessage(Message(
    id: "msg-1",
    role: .user,
    content: "Hello, how are you?",
    createdAt: Date()
))

session.addMessage(Message(
    id: "msg-2",
    role: .assistant,
    content: "I am good, thank you!",
    createdAt: Date()
))
```

`addMessage` 是同步返回的。预加载的结果会稍后通过 `onEvent` 回调送达（`.filled`、`.noFill`、`.error` ……）——不会通过返回值返回。

## 3. 渲染广告

使用 `session.createAd(messageId:)` 为某条助手消息获取一个 `Ad`，再用 `InlineAdUIView` 渲染。

`createAd` 是幂等的：对同一个 `messageId` 多次调用会返回同一个 `Ad`。请缓存返回的实例，避免 view controller / cell 复用时重复创建。

```swift theme={null}
let ad = session.createAd("msg-2")
let adView = InlineAdUIView(ad: ad)
adView.translatesAutoresizingMaskIntoConstraints = false
container.addSubview(adView)

// 在 UITableView / UICollectionView 的 cell 中，观察高度变化以正确撑开尺寸。
adView.onHeightChange = { [weak self] height in
    self?.updateRowHeight(height)
}
```

## 4. 释放资源

对话结束或视图消失时调用 `destroy()`。该方法是幂等的，且是取消待处理网络请求、释放 web view 资源的必要操作。

```swift theme={null}
session.destroy()
```

## 订阅事件

接收 `AdEvent` 有两种等价方式——任选其一。

### `onEvent` 回调

```swift theme={null}
let session = KontextAds.createSession(SessionOptions(
    publisherToken: "...",
    userId: "...",
    conversationId: "...",
    onEvent: { event in
        switch event {
        case .filled(let data):
            print("ad filled: \(data.bidId) revenue=\(data.revenue ?? 0)")
        case .clicked(let data):
            print("clicked: \(data.url)")
        default: break
        }
    }
))
```

### Combine publisher

```swift theme={null}
import Combine

var cancellables: Set<AnyCancellable> = []

session.eventPublisher
    .sink { event in
        print("event: \(event.name)")
    }
    .store(in: &cancellables)
```

两种方式都会在主线程上送达相同的事件。完整事件列表见 [API 参考](/sdk/swift/api)。
