Настройка Pushwoosh InboxKit для iOS
Доступно с iOS SDK 7.0.40.
Pushwoosh InboxKit предоставляет современный экран входящих сообщений на базе UIKit поверх существующего бэкенда для входящих сообщений. Шесть стандартных макетов ячеек охватывают распространённые формы карточек контента — от простых баннеров до каруселей изображений, встроенного видео и пропусков Apple Wallet. Встроенные CTA-кнопки обрабатывают самые распространённые взаимодействия, а весь интерфейс открыт для создания подклассов, если вам нужен индивидуальный дизайн.

Стандартная лента InboxKit с карточками баннер, с подписью, классическая, карусель, видео и Apple Wallet.
Когда использовать InboxKit
Anchor link toИспользуйте InboxKit для любой новой интеграции с iOS. Это рекомендуемая замена для устаревшего модуля PushwooshInboxUI на Objective-C.
InboxKit предоставляет вам:
- Шесть встроенных типов ячеек — баннер, с подписью, классическая, карусель, видео и Apple Wallet — выбираются для каждого сообщения через
displayTypeв полезной нагрузке или принудительно устанавливаются из кода черезattributes.forceCellKind. Полный список см. в разделе Типы карточек. (Карточка Apple Wallet доступна только на iOS.) - Встроенные CTA-кнопки с типизированным перечислением
PushwooshInboxButtonAction(openURL,dismiss,markRead,custom). SDK автоматически обрабатывает первые три. Ваш делегат направляетcustomв вашу собственную логику. - Поддержка закрепления: сообщения с
actionParams["pinned"] == trueподнимаются в верхнюю часть ленты и отображают значок булавки. - Свайп для удаления, потяните для обновления, автоматическая отметка как прочитанное при исчезновении — всё это можно переключать через
PushwooshInboxKitAttributes. - Постоянное хранилище: удаления и состояние прочтения сохраняются после перезапуска процесса, даже если сетевой вызов ещё не был подтверждён.
- Открытый базовый класс
PushwooshInboxCellдля полностью настраиваемых макетов.
Серверный контракт не изменился — тот же бэкенд для входящих сообщений Pushwoosh, полезные нагрузки и инструменты панели управления работают, как и раньше.
Выберите способ интеграции
Anchor link to- Настройка InboxKit с помощью Swift Package Manager — рекомендуется для новых проектов.
- Настройка InboxKit с помощью CocoaPods — для проектов, уже использующих CocoaPods.
Типы карточек
Anchor link toInboxKit выбирает макет ячейки для каждого сообщения. Стандартный резолвер читает displayType из полезной нагрузки пуша — поместите его в объект data, который SDK доставляет в actionParams. Если displayType отсутствует, резолвер использует эвристику: изображение без заголовка → баннер, изображение с заголовком → с подписью, иначе классическая. Чтобы принудительно задать один макет для всей ленты из кода, установите attributes.forceCellKind.
Каждый расширенный макет деградирует корректно: если требуемая полезная нагрузка отсутствует или повреждена, карточка переключается на classic вместо пустого плейсхолдера (в лог записывается WARN).
displayType | Макет | Обязательное поле полезной нагрузки | Деградирует до |
|---|---|---|---|
banner | Изображение на всю ширину, без текста | image (inbox_image или data.image) | classic, если нет изображения |
captioned | Изображение сверху, заголовок и текст ниже | image (inbox_image или data.image) | classic, если нет изображения |
classic | Цветной аватар с инициалом + заголовок + текст | — | — |
carousel | Галерея из нескольких изображений со свайпом | data.carousel (массив слайдов) | classic, если нет слайдов |
video | Постер с значком воспроизведения, полноэкранный плеер по нажатию | data.video (url + необязательный poster) | classic, если нет дескриптора |
wallet | Кнопка «Добавить в Apple Wallet» (только iOS) | data.wallet (URL .pkpass) | classic, если нет URL пропуска |






Карточки баннер, с подписью и классическая управляются стандартными полями сообщения (изображение, заголовок, текст) плюс необязательный массив buttons — см. Добавление встроенных CTA-кнопок. Карточки карусель, видео и Apple Wallet содержат дополнительные структурированные данные внутри data, описанные ниже.
Карточка-карусель
Anchor link toКарусель отображает несколько изображений из одного сообщения — галерею со свайпом с необязательными подписями и целевыми ссылками для каждого слайда. Слайды находятся в data.carousel. Каждый слайд требует image. Поля title (подпись поверх изображения) и url (deep link при нажатии) необязательны. Слайд без изображения пропускается. Нажатие на слайд без url передаётся стандартному действию строки.
{ "request": { "application": "XXXXX-XXXXX", "auth": "API_TOKEN", "notifications": [{ "send_date": "now", "ios_title": "New arrivals", "content": "Swipe through this week's drops", "inbox_days": 7, "data": { "displayType": "carousel", "carousel": [ { "image": "https://cdn.example.com/inbox/1.jpg", "title": "New in", "url": "myapp://product/1" }, { "image": "https://cdn.example.com/inbox/2.jpg", "title": "On sale", "url": "myapp://product/2" }, { "image": "https://cdn.example.com/inbox/3.jpg" } ] }, "platforms": [1] }] }}Видеокарточка
Anchor link toВидеокарточка показывает постер с значком воспроизведения. Нажатие открывает полноэкранный плеер (со звуком, даже при включённом беззвучном режиме). Дескриптор находится в data.video: url обязателен и должен быть потоком или файлом по http/https. poster — необязательное изображение предпросмотра.
{ "request": { "application": "XXXXX-XXXXX", "auth": "API_TOKEN", "notifications": [{ "send_date": "now", "ios_title": "Watch the reveal", "content": "Tap to play", "inbox_days": 7, "data": { "displayType": "video", "video": { "url": "https://cdn.example.com/inbox/clip.mp4", "poster": "https://cdn.example.com/inbox/poster.jpg" } }, "platforms": [1] }] }}Карточка Apple Wallet
Anchor link toКарточка Apple Wallet показывает необязательное изображение, заголовок и текст над официальной кнопкой Add to Apple Wallet. Нажатие на кнопку загружает .pkpass и открывает системный лист добавления пропусков. Используйте её для доставки купонов, карт лояльности, билетов или посадочных талонов прямо из входящих. Карточка доступна только на iOS и Mac Catalyst. На других платформах сообщение отображается как классическая карточка.
URL пропуска находится в data.wallet — как строка или как объект с полем pass. Необязательный data.image добавляет изображение-герой. Кнопка скрывается автоматически, если нет URL пропуска или устройство не может добавлять пропуски.
{ "request": { "application": "XXXXX-XXXXX", "auth": "API_TOKEN", "notifications": [{ "send_date": "now", "ios_title": "Your loyalty card is ready", "content": "Add it to Apple Wallet in one tap", "inbox_days": 7, "data": { "displayType": "wallet", "image": "https://cdn.example.com/inbox/loyalty.png", "wallet": "https://passes.example.com/v1/passes/pass.com.example.loyalty/abc123?token=…" }, "platforms": [1] }] }}Результат передаётся вашему делегату:
extension MyInboxHost: PushwooshInboxKitDelegate {
func inboxKit(_ vc: PushwooshInboxKitViewController, didAddWalletPassFor message: PWInboxMessageProtocol) { // Пропуск теперь в Wallet пользователя — при желании покажите подтверждение. }
func inboxKit(_ vc: PushwooshInboxKitViewController, didFailToAddWalletPassFor message: PWInboxMessageProtocol, error: Error?) { // Загрузка не удалась — покажите повтор, запишите в лог и т. д. }}Оба колбэка необязательны (имеют пустые реализации по умолчанию). Если пользователь отменяет системный лист, колбэк не вызывается — это ни успех, ни ошибка.
Специальные возможности
Anchor link toЯчейки InboxKit готовы к VoiceOver из коробки. Карточки баннер, с подписью и классическая передают заголовок, текст и дату через базовые метки. Встроенные CTA-кнопки озвучивают собственные заголовки. Расширенные карточки добавляют явную семантику:
- Видео — постер представлен как единый элемент-кнопка с меткой «Play video» (traits
.button+.startsMediaSession), поэтому VoiceOver объявляет его как медиаэлемент, а не как обычное изображение. - Карусель — каждый слайд является элементом-кнопкой, чья accessibility-метка — подпись слайда или «Slide», если подписи нет. Индикатор страниц объявляет текущую позицию как «n из total».
- Apple Wallet — кнопка Add to Apple Wallet — это стандартная
PKAddPassButtonот Apple со своей локализованной меткой VoiceOver.
Для UI-тестирования и автоматизации заданы два стабильных accessibilityIdentifier: inboxkit.video.play на видеопостере и inboxkit.wallet.add на кнопке Wallet.
Чтение пользовательских данных из сообщения
Anchor link toЧтобы пуш появился во входящих, запрос createMessage в Messages API должен включать 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": { "displayType": "captioned", "promo_id": "SUMMER2026", "screen": "promo_details" }, "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 }
// Пользовательский объект `data` приходит под ключом "u" — // либо как вложенный словарь, либо как строка в формате JSON, // в зависимости от того, как была сформирована полезная нагрузка. 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 // мы обработали нажатие; SDK не должен выполнять действие по умолчанию } return true }}Тот же поиск actionParams["u"] работает внутри inboxKit(_:didTapButton:onMessage:) для встроенных CTA-кнопок. Для типизированных случаев CTA (openURL, dismiss, markRead) SDK уже выполняет действие по умолчанию — верните true, чтобы сохранить это поведение, или false, чтобы подавить его и запустить собственную логику.
Добавление встроенных CTA-кнопок
Anchor link toСообщение может содержать до трёх встроенных кнопок призыва к действию (CTA). Кнопки находятся вместе с другими пользовательскими данными внутри 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": { "displayType": "captioned", "promo_id": "SUMMER2026", "buttons": [ { "title": "Claim", "url": "https://example.com/promo/SUMMER2026" }, { "title": "Read", "action": "markRead" }, { "title": "Save", "action": "custom", "tag": "save_promo" } ] }, "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): // Поведение по умолчанию подходит — пусть SDK откроет URL. return true
case .dismiss, .markRead: // SDK обрабатывает оба случая. Верните false, если хотите переопределить. return true
case .custom(let payload): // Пользовательская кнопка, определённая маркетологом. Выполняйте диспетчеризацию по согласованному ключу. if let tag = payload["tag"] as? String { switch tag { case "save_promo": saveCurrentPromoLocally(message: message) default: break } } return true // игнорируется для custom — SDK никогда не выполняет здесь действие по умолчанию } }}