> ## 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.

# API 参考

> Swift 视频 SDK 的每个类型、方法、回调和错误。

所有公开类型都是 `@MainActor`。时间单位为秒；价格为 CPM。

## `KontextVideo`

入口，唯一实例：`KontextVideo.shared`。

**initialize(with:completion:)** `method`

启动 SDK：保存配置并在后台联系服务器。启动时调用一次；之后的调用被忽略。服务器回答或失败后，`completion` 在主线程运行。

```swift theme={null}
KontextVideo.shared.initialize(with: .init(publisherToken: "…")) { state in … }
```

**initialize(with:)** `async method`

`async` 代码中的同一调用；返回结果 `State`：`.enabled`、`.disabled(reason:)`（服务器在本次启动中关闭了 SDK）或 `.failed`（无法连接服务器；每次广告请求会先重试）。

```swift theme={null}
let state = await KontextVideo.shared.initialize(with: .init(publisherToken: "…"))
```

**user** `User?`

你了解的观众信息。一旦知道就设置；下一次广告请求会携带它。

**regulatory** `Regulatory?`

隐私和同意信号。SDK 从你的同意平台的共享存储读取 TCF 值；在这里设置它没写的部分（COPPA 始终来自你）。

**variantId** `String?`

选择渲染哪种广告，用于与 Kontext 约定的 A/B 测试。

**version** `String`

SDK 版本，与每次请求发送的一致。用于你的日志和崩溃报告。

## `KontextVideo.Configuration`

**publisherToken** `String` *required*

标识发布方。不是密钥。你会得到一个用于测试的 `{publisher}-dev` token 和一个生产 token。

**requestTrackingAuthorization** `Bool`

SDK 是否在第一次广告请求前自行展示 App Tracking Transparency 对话框。默认 `false`：由应用决定对话框何时出现。

**logLevel** `LogLevel`

SDK 向控制台输出多少：`.none`、`.error`、`.warning`（默认）、`.info`、`.debug`。

## `KontextVideo.Content`

广告对应的视频。除 `id` 和 `language` 之外的一切都是情境信号。

**id** `String` *required*

你自己为视频设定的 id，任意格式。

**language** `String` *required*

BCP-47，例如 `"en"` 或 `"pt-BR"`。

**mediaRating** `Rating?`

`.safe` 或 `.explicit`。不知道时留空；露骨内容不会得到要求安全内容的广告。

**url** `URL?`

剧集的公开页面。

**title** `String?`

**categories** `[String]?`

IAB 内容分类 3.0 的 id。

**description** `String?`

**duration** `TimeInterval?`

秒。

**episodeNumber** `Int?`

**keywords** `[String]?`

**genre** `String?`

例如 `"romance"`。

**series** `Series?`

**属性**

**id** `String` *required*

**title** `String?`

**description** `String?`

**episodeCount** `Int?`

总集数。

## `KontextVideo.User`

**id** `String?`

你自己为观众设定的唯一标识。

**email** `String?`

明文邮箱，在同意允许时。服务器到达时即哈希，从不存储明文。

**emailHash** `String?`

规范化地址（去空格、小写、UTF-8）的 SHA-256，64 位小写十六进制。

**keywords** `[String]?`

你用自己的话描述的观众兴趣。

## `KontextVideo.Regulatory`

**gdpr** `Bool?`

请求是否受 GDPR 约束。

**gdprConsent** `String?`

IAB TCF v2 同意字符串。

**coppa** `Bool?`

请求是否受 COPPA 约束。无法从设备读取。

**gpp** `String?`

IAB Global Privacy Platform 字符串。

**gppSid** `[Int]?`

适用的 GPP 章节 id。

**usPrivacy** `String?`

旧版 IAB US Privacy（CCPA）字符串。

## `AdFormat`

`.overlay`、`.pause`、`.linear`。

## `VideoAds`

一个视频的广告。每集创建一个；创建不发送任何请求。任意数量可同时存在。

**init(content:)** `initializer`

```swift theme={null}
let ads = VideoAds(content: KontextVideo.Content(id: "episode-1", language: "en"))
```

**format(\_:)** `async throwing method`

一种格式的广告：在它未展示且有效期间是上次的同一个 `VideoAd`，否则是新的。请求失败时，持有的广告仍有效则返回它。

抛出 `noFill(reason:)`、`request(_:)`、`destroyed`、`disabled(reason:)`、`notInitialized`、`initializationFailed`，或你的任务被取消时抛出 `CancellationError`（请求本身继续）。

```swift theme={null}
let ad = try await ads.format(.pause)
```

**trackPlayhead(of:)** `method`

SDK 需要时从这个 `AVPlayer` 读取内容播放位置。弱持有；不添加观察者。在本集开始在该播放器中播放时调用。

**playheadProvider** `(() -\> TimeInterval)?`

SDK 读取内容播放位置（秒）的地方，适用于任何播放器。`trackPlayhead(of:)` 会为你设置它。未设置：0，即预取应发送的值。

**destroy()** `method`

释放视频的广告。所有持有的广告被丢弃并告知广告主未使用；屏幕上的广告以 `didEnd(.destroyed)` 结束；进行中的 `format()` 或 `load()` 抛出 `destroyed`。幂等。释放对象也会尽力做同样的事。

**content** `Content`

创建时传入的内容。

**isDestroyed** `Bool`

`destroy()` 之后为 `true`。

## `VideoAd`

一个广告，从服务器的回答到呈现结束。看它的事实，然后 `load()` 或 `reject()`，然后 `show()`，然后恰好一次 `didEnd`。最多展示一次。

### 事实

**id** `String`

**format** `AdFormat`

**price** `Price`

**属性**

**value** `Double`

CPM。

**currency** `String`

ISO 4217。

**precision** `Precision`

`.exact`（广告主的成交价）或 `.estimated`（自有需求，预估）。

**duration** `TimeInterval?`

叠加广告：SDK 让它停留多久，`nil` 表示直到被结束。线性广告：视频长度。暂停广告始终为 `nil`。

### 状态

**isLoaded** `Bool`

`load()` 已完成。

**isReady** `Bool`

已加载、未展示、未拒绝、未过期：可以尝试 `show()`。是提示而非保证；`show()` 仍会测量视图。

**invalidationReason** `InvalidationReason?`

广告在展示前结束的原因（如果发生）：`.expired`、`.rejected`、`.destroyed`、`.creativeFailed(code:)`。

**wasShown** `Bool`

成功 `show()` 之后为 `true`。

**hasEnded** `Bool`

广告开始结束后为 `true`。叠加和暂停广告随即收到 `didEnd`，线性广告在关闭完成后收到。

**isCompleted** `Bool`

素材是否播放到底：视频到达完成、叠加广告时长到期。与它如何离开屏幕是两回事。

**delegate** `VideoAdDelegate?`

接收广告的各个时刻。弱持有。

### 方法

**load()** `async throwing method`

在屏幕外渲染素材，页面报告就绪后返回，之后 `show()` 是即时的。抛出 `creativeFailed(code:)`、`invalidated(_:)`、`destroyed`、`disabled(reason:)`。

**reject(\_:)** `throwing method`

你不会使用这个广告；服务器告知广告主。`.notUsed`（默认）或 `.outbid(winningPrice:winner:)`。`show()` 之前都可用，无论是否已加载。之后抛出 `alreadyShown`，广告已结束时抛出对应的失效原因。

```swift theme={null}
try ad.reject(.outbid(winningPrice: 2.4, winner: "Other network"))
```

**show(in:)** `throwing method`

在 `view`（内容播放所在的视图）中展示叠加或暂停广告。广告在屏幕上即成功。抛错时什么都没展示：`notLoaded`、`alreadyShown`、`anotherAdShowing`、`invalidated(_:)`、`destroyed`、`disabled(reason:)`、`presenterUnavailable`（视图不在屏幕上）和 `containerTooSmall(minimum:)`（两者广告都保持就绪）、`wrongPresentation`（线性广告）。

**show(from:)** `throwing method`

全屏呈现线性广告：从给定的视图控制器，或用 `nil`（默认）从关键窗口最上层的视图控制器。抛错时什么都没展示：上述错误加上 `presenterUnavailable`（广告保持就绪）、`wrongPresentation`（叠加或暂停广告）。

```swift theme={null}
try linear.show()
try linear.show(from: viewController)
```

**dismiss()** `throwing method`

由你结束叠加或暂停广告；随后同步收到 `didEnd(.dismissed)`。`show()` 之前和 `didEnd` 之后不做任何事。对线性广告抛出 `wrongPresentation`，它只由 SDK 关闭。

## `VideoAdDelegate`

在主 actor 上送达。每个方法都有空的默认实现；实现你需要的。`show()` 之前什么都不会送达。

**videoAdDidRecordImpression(\_:)** `method`

曝光已计数：图片在 `show()` 时，视频在第一帧时。最多一次；也可能不发生。

**videoAd(\_:didClick:)** `method`

观众点击了广告；SDK 打开了 `click.url`，对于应用安装广告则在你的应用内打开 App Store 产品页。`click.area` 为 `.creative`、`.endCard` 或 `.other(String)`。每次点击一次。

**videoAd(\_:didFail:)** `method`

呈现开始后素材失败或卡住。`didEnd(.failed)` 立即跟随。

**videoAd(\_:didEnd:)** `method`

呈现结束。每次成功的 `show()` 恰好一次，总是最后一个。无论原因如何，在这里恢复你的内容。

## `VideoAd.EndReason`

| 原因 | 何时 |
| - | - |
| `.completed` | 广告自行结束：叠加广告时长到期，线性视频播完且没有结尾卡片。 |
| `.skipped` | 观众在视频结束前点了线性广告的跳过。 |
| `.closed` | 观众点了广告的关闭控件（叠加或暂停卡片的 ✕，线性结尾卡片的 ✕）。暂停卡片的 ✕ 会让内容保持暂停。 |
| `.resumed` | 观众在全屏暂停广告上点了继续播放。 |
| `.failed` | 素材失败或卡住，或全屏广告无法呈现；`didFail` 先到。 |
| `.interrupted` | 呈现因 SDK 之外的原因消失：视图离开窗口、呈现它的控制器被关闭。大约半秒内察觉。 |
| `.dismissed` | 你调用了 `dismiss()`。 |
| `.destroyed` | 你调用了 `destroy()`。 |

## `VideoAdError`

抛出，从不通过代理送达：抛错意味着屏幕上什么都没发生，也没有任何事件触发。

| 错误 | 何时 |
| - | - |
| `.notInitialized` | 从未调用 `initialize(with:)`。 |
| `.initializationFailed` | 无法连接服务器，重试也失败了。每次 `format()` 都会重试。 |
| `.destroyed` | `VideoAds` 已销毁。为下一个内容创建新的。 |
| `.disabled(reason:)` | 服务器在本次启动中关闭了 SDK。 |
| `.noFill(reason:)` | 服务器没有该格式的广告。见 `NoFillReason`。 |
| `.request(Error)` | 请求失败且没有持有该格式的有效广告。 |
| `.creativeFailed(code:)` | 素材在十秒活跃时间内未加载完成；`code` 是 VAST 错误码。 |
| `.alreadyShown` | 已展示过一次；不能再展示或拒绝。 |
| `.anotherAdShowing` | 任意 `VideoAds` 的另一个广告在屏幕上。此广告保持就绪。 |
| `.invalidated(reason)` | 广告在展示前结束：`.expired`、`.rejected`、`.destroyed`、`.creativeFailed(code:)`。 |
| `.notLoaded` | `load()` 完成前就 `show()`。广告保持原状。 |
| `.containerTooSmall(minimum:)` | 广告必须缩小到 50% 以下才能放入。广告保持就绪。 |
| `.presenterUnavailable` | 现在屏幕上没有东西能承载广告：没有视图控制器能呈现线性广告，或为叠加/暂停广告给出的视图不在窗口中。广告保持就绪。 |
| `.wrongPresentation` | 对叠加或暂停广告调用 `show()`，对线性广告调用 `show(in:)` 或 `dismiss()`。 |

## `VideoAdError.NoFillReason`

| 原因 | 含义 |
| - | - |
| `.noAdMatch` | 没有广告活动匹配内容、观众或时机 |
| `.frequencyCap` | 观众达到了为你的应用设置的频次上限。仅在配置了上限时出现 |
| `.contentUnsafe` | 内容不满足品牌安全 |
| `.geoRestricted` | 不在该国家投放 |
| `.formatNotEnabled` | 你的应用未启用该格式 |
| `.temporaryError` | 我们这边的问题；稍后再请求 |
| `.alreadyUsed` | SDK 自身的原因：回答是你刚展示或拒绝的广告 |
| `.unknown` | 服务器没有给出原因 |
| `.other(String)` | 此 SDK 版本不认识的原因，原样透传 |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.