跳到内容

设置 Pushwoosh InboxKit iOS

自 iOS SDK 7.0.40 起可用。

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

InboxKit 信息流,显示横幅、带标题和经典卡片,以及内联按钮和未读指示器

默认的 InboxKit 信息流,包含横幅、带标题和经典卡片。

何时使用 InboxKit

Anchor link to

对于任何新的 iOS 集成,请使用 InboxKit。它是旧版 Objective-C PushwooshInboxUI 模块的推荐替代品。

InboxKit 为您提供:

  • 三种内置单元格类型——横幅、带标题、经典——可通过推送负载中的 actionParams["displayType"] 为每条消息选择,或通过代码中的 attributes.forceCellKind 强制指定。
  • 内联 CTA 按钮,带有类型化的 PushwooshInboxButtonAction 枚举(openURLdismissmarkReadcustom)。SDK 会自动处理前三种;您的委托将 custom 路由到您自己的逻辑。
  • 置顶支持:带有 actionParams["pinned"] == true 的消息会浮动到信息流顶部并显示一个图钉图标。
  • 滑动删除、下拉刷新、消失时自动标记为已读——所有这些都可以通过 PushwooshInboxKitAttributes 进行切换。
  • 持久化存储:即使网络调用尚未被确认,删除和已读状态在进程重启后依然存在。
  • 一个开放的 PushwooshInboxCell 基类,用于完全自定义的布局。

服务器契约保持不变——同样的 Pushwoosh 收件箱后端、负载和仪表板工具都和以前一样工作。

选择您的集成方法

Anchor link to

从消息中读取自定义数据

Anchor link to

要使推送出现在收件箱中,Messages APIcreateMessage 请求必须包含 inbox_imageinbox_dateinbox_days——没有这些字段之一,推送将作为常规通知发送,永远不会到达收件箱信息流。自由格式的自定义数据放在 data 键下,SDK 将其作为 u 参数传递给客户端:

POST https://api.pushwoosh.com/json/1.3/createMessage
{
"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 情况(openURLdismissmarkRead),SDK 已经执行了默认操作——返回 true 以保留该行为,或返回 false 以禁止它并运行您自己的逻辑。

添加内联 CTA 按钮

Anchor link to

一条消息最多可以携带三个内联的行动号召 (call-to-action) 按钮。按钮与其他自定义数据一起存在于 data 内部,作为一个 buttons 数组。SDK 会在带标题和经典单元格内自动渲染它们:

POST https://api.pushwoosh.com/json/1.3/createMessage
{
"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]
}]
}
}

每个按钮对象都有以下字段:

字段类型说明
titlestring必需。可见的按钮标签。
urlstring一个非空且可解析的 URL 会产生一个 openURL 操作。除非您的委托禁止,否则 SDK 会通过 UIApplication.shared.open 打开它。
actionstring显式操作令牌:dismiss(从信息流中移除消息)、markRead(将消息标记为已读)或 custom(由宿主处理)。不区分大小写。
其他任何内容anyactioncustom 时,按钮对象上除了 titleaction 之外的每个键都会作为自定义负载转发给您的委托——与市场营销人员商定一个键(例如 tag)并据此进行分发。

解析优先级:首先是显式的 action 令牌,然后是非空的 url,否则按钮将归入 custom 类型,并携带完整的负载(减去 titleaction)。

从您的委托中拦截点击。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
}
}
}