设置 Pushwoosh InboxKit iOS
自 iOS SDK 7.0.40 起可用。
Pushwoosh InboxKit 在现有收件箱后端的基础上提供了一个现代化的 UIKit 收件箱屏幕。三种默认的单元格布局涵盖了常见的 Braze 风格内容卡片形状,内联 CTA 按钮处理最常见的交互,并且整个界面都开放用于子类化,以满足您定制外观的需求。

默认的 InboxKit 信息流,包含横幅、带标题和经典卡片。
何时使用 InboxKit
Anchor link to对于任何新的 iOS 集成,请使用 InboxKit。它是旧版 Objective-C PushwooshInboxUI 模块的推荐替代品。
InboxKit 为您提供:
- 三种内置单元格类型——横幅、带标题、经典——可通过推送负载中的
actionParams["displayType"]为每条消息选择,或通过代码中的attributes.forceCellKind强制指定。 - 内联 CTA 按钮,带有类型化的
PushwooshInboxButtonAction枚举(openURL、dismiss、markRead、custom)。SDK 会自动处理前三种;您的委托将custom路由到您自己的逻辑。 - 置顶支持:带有
actionParams["pinned"] == true的消息会浮动到信息流顶部并显示一个图钉图标。 - 滑动删除、下拉刷新、消失时自动标记为已读——所有这些都可以通过
PushwooshInboxKitAttributes进行切换。 - 持久化存储:即使网络调用尚未被确认,删除和已读状态在进程重启后依然存在。
- 一个开放的
PushwooshInboxCell基类,用于完全自定义的布局。
服务器契约保持不变——同样的 Pushwoosh 收件箱后端、负载和仪表板工具都和以前一样工作。
选择您的集成方法
Anchor link to- 使用 Swift Package Manager 设置 InboxKit — 推荐用于新项目。
- 使用 CocoaPods 设置 InboxKit — 适用于已在使用 CocoaPods 的项目。
从消息中读取自定义数据
Anchor link to要使推送出现在收件箱中,Messages API 的 createMessage 请求必须包含 inbox_image、inbox_date 或 inbox_days——没有这些字段之一,推送将作为常规通知发送,永远不会到达收件箱信息流。自由格式的自定义数据放在 data 键下,SDK 将其作为 u 参数传递给客户端:
{ "request": { "application": "XXXXX-XXXXX", "auth": "API_TOKEN", "notifications": [{ "send_date": "now", "ios_title": "Summer sale", "content": "30% off everything — limited time only", "inbox_image": "https://cdn.example.com/inbox/summer.png", "inbox_days": 7, "data": { "promo_id": "SUMMER2026", "screen": "promo_details" }, "ios_root_params": { "displayType": "captioned" }, "platforms": [1] }] }}SDK 通过 actionParams 在收件箱消息上暴露该对象。当用户点击行或内联 CTA 时,从委托中读取它:
extension MyInboxHost: PushwooshInboxKitDelegate {
func inboxKit(_ vc: PushwooshInboxKitViewController, didSelect message: PWInboxMessageProtocol) -> Bool { guard let params = message.actionParams as? [String: Any] else { return true }
// The custom `data` object arrives under the "u" key — // either as a nested dictionary or as a JSON-encoded string, // depending on how the payload was built upstream. let custom: [String: Any]? = { if let dict = params["u"] as? [String: Any] { return dict } if let raw = params["u"] as? String, let bytes = raw.data(using: .utf8), let parsed = try? JSONSerialization.jsonObject(with: bytes) as? [String: Any] { return parsed } return nil }()
if let promoId = custom?["promo_id"] as? String { navigateToPromo(promoId) return false // we handled the tap; SDK should not run the default action } return true }}对于内联 CTA 按钮,在 inboxKit(_:didTapButton:onMessage:) 中使用相同的 actionParams["u"] 查找方式。对于类型化的 CTA 情况(openURL、dismiss、markRead),SDK 已经执行了默认操作——返回 true 以保留该行为,或返回 false 以禁止它并运行您自己的逻辑。
添加内联 CTA 按钮
Anchor link to一条消息最多可以携带三个内联的行动号召 (call-to-action) 按钮。按钮与其他自定义数据一起存在于 data 内部,作为一个 buttons 数组。SDK 会在带标题和经典单元格内自动渲染它们:
{ "request": { "application": "XXXXX-XXXXX", "auth": "API_TOKEN", "notifications": [{ "send_date": "now", "ios_title": "New promo card", "content": "Tap a button to claim or save", "inbox_image": "https://cdn.example.com/inbox/promo.png", "inbox_days": 7, "data": { "promo_id": "SUMMER2026", "buttons": [ { "title": "Claim", "url": "https://example.com/promo/SUMMER2026" }, { "title": "Read", "action": "markRead" }, { "title": "Save", "action": "custom", "tag": "save_promo" } ] }, "ios_root_params": { "displayType": "captioned" }, "platforms": [1] }] }}每个按钮对象都有以下字段:
| 字段 | 类型 | 说明 |
|---|---|---|
title | string | 必需。可见的按钮标签。 |
url | string | 一个非空且可解析的 URL 会产生一个 openURL 操作。除非您的委托禁止,否则 SDK 会通过 UIApplication.shared.open 打开它。 |
action | string | 显式操作令牌:dismiss(从信息流中移除消息)、markRead(将消息标记为已读)或 custom(由宿主处理)。不区分大小写。 |
| 其他任何内容 | any | 当 action 是 custom 时,按钮对象上除了 title 和 action 之外的每个键都会作为自定义负载转发给您的委托——与市场营销人员商定一个键(例如 tag)并据此进行分发。 |
解析优先级:首先是显式的 action 令牌,然后是非空的 url,否则按钮将归入 custom 类型,并携带完整的负载(减去 title 和 action)。
从您的委托中拦截点击。button.action 属性是类型化的 PushwooshInboxButtonAction 枚举:
extension MyInboxHost: PushwooshInboxKitDelegate {
func inboxKit(_ vc: PushwooshInboxKitViewController, didTapButton button: PushwooshInboxButton, onMessage message: PWInboxMessageProtocol) -> Bool { switch button.action { case .openURL(let url): // Default behavior is fine — let SDK open the URL. return true
case .dismiss, .markRead: // SDK handles both. Return false if you want to override. return true
case .custom(let payload): // Marketer-defined custom button. Dispatch on a key you agreed on. if let tag = payload["tag"] as? String { switch tag { case "save_promo": saveCurrentPromoLocally(message: message) default: break } } return true // ignored for custom — SDK never runs a default action here } }}