--- title: "iOS custom inbox mobile" slug: "ios-custom-inbox" updated: 2026-06-09T22:54:06Z published: 2026-06-09T22:54:06Z canonical: "docs.xtremepush.com/ios-custom-inbox" stale: true --- > ## Documentation Index > Fetch the complete documentation index at: https://docs.xtremepush.com/llms.txt > Use this file to discover all available pages before exploring further. # iOS custom inbox mobile Implement a custom inbox in your iOS Application By default, the XtremePush SDK renders the inbox as a built-in WebView. A **custom inbox** replaces that with your own native UI, giving you full control over layout and interaction. When you implement a custom inbox, the SDK continues to handle: - Fetching and paginating inbox messages from the platform - Deleting messages - Tracking badge counts - Sending opened/clicked analytics events (when you call the reporting methods) Your app becomes responsible for: - Rendering each message (title, body, image, icon, card vs alert layout) - Handling tap actions (deeplink navigation, opening URLs) - Calling the reporting methods at the right time ## Retrieve inbox messages The `inboxListWithOffset:limit:callback:` method asynchronously fetches a list of inbox items. Use the `offset` and `limit` parameters to support pagination. **Swift** ```swift XPush.inboxList(withOffset: 0, limit: 20) { (list, badge, error) in    if let list = list {        // render list    } else if let error = error {        // handle error    } } ``` **Objective-C** ```objectivec [XPush inboxListWithOffset:0 limit:20 callback:^(NSArray * _Nullable list, NSUInteger badge, NSError * _Nullable error) {    if (list != nil) {        // render list    } else if (error != nil) {        // handle error    } }]; ``` ## XPInboxItem Each item in the returned array is an `XPInboxItem` with the following properties: | Property | Type | Description | | --- | --- | --- | | `identifier` | `NSInteger` | Unique message ID | | `isOpened` | `BOOL` | Whether the message has been marked as opened | | `isClicked` | `BOOL` | Whether the message has been marked as clicked | | `isDelivered` | `BOOL` | Whether the message has been delivered | | `createTimestamp` | `NSNumber *` | Unix timestamp of when the message was created | | `expirationTimestamp` | `NSNumber *` | Unix timestamp of expiry, or nil if the message does not expire | | `style` | `XPInboxItemStyle *` | Background and title background colour values | | `isCard` | `BOOL` | `YES` for card layout (full-width banner image), `NO` for alert layout (small thumbnail) | | `response` | `XPMessageResponse *` | Contains the message content and tap action — see below | ## Working with message content Each `XPInboxItem` exposes its content through `item.response`, which contains two objects: `item.response.message` (`XPMessage`) — the message content: | Property | Type | Set by | | --- | --- | --- | | `identifier` | `NSString *` | Platform (system-assigned) | | `campaignIdentifier` | `NSString *` | Platform (system-assigned) | | `title` | `NSString *` | Campaign — Push title field | | `text` | `NSString *` | Campaign — Push text / body field | | `icon` | `NSString *` | Campaign — Push icon field (falls back to app icon, then project icon, if not set) | | `data` | `NSDictionary *` | Campaign — Custom payload fields (payload_add), with SDK-reserved keys removed | | `payload` | `NSDictionary *` | Full raw message dictionary — use when you need direct access to fields not surfaced above | `item.response.action` (`XPAction`) — the tap action: | Property | Type | Set by | | --- | --- | --- | | `deeplink` | `NSString *` | Campaign — present when action type is **Deeplink**; `nil` otherwise | | `url` | `NSURL *` | Campaign — present when action type is **URL**; `nil` otherwise | | `identifier` | `NSString *` | Platform — pass to reporting methods | `deeplink` and `url` are mutually exclusive — only one will be set depending on the action type configured in the campaign. If neither is set, the message has no tap action. **Layout and style** are on the item: | Property | Type | Set by | | --- | --- | --- | | `isCard` | `BOOL` | Campaign — inbox type setting. `YES` = card layout (full-width banner), `NO` = alert layout (small thumbnail) | | `style.background` | `NSString *` | Campaign — inbox style background colour | | `style.titleBackground` | `NSString *` | Campaign — inbox style title bar colour | ### Custom payload fields If your inbox UI requires data beyond the standard fields — for example a CTA label, a secondary URL, or a badge value — add custom fields in the campaign's **Custom payload** (payload_add) section. These are delivered in `item.response.message.data` as a key-value dictionary, with all SDK-reserved keys already stripped out. **Swift** ```swift // Reading a custom payload field if let ctaLabel = item.response.message.data?["cta_label"] as? String {    button.setTitle(ctaLabel, for: .normal) } ``` **Objective-C** ```objectivec NSString *ctaLabel = item.response.message.data[@"cta_label"]; if (ctaLabel) {    [button setTitle:ctaLabel forState:UIControlStateNormal]; } ``` > [!NOTE] > Campaign Templates > > Because your custom inbox UI is built to render a specific content structure, consider defining a reusable inbox campaign template in the Xtremepush platform that consistently populates the fields your UI expects. For example, if your card layout always shows a title, body, background colour, deeplink action, and a custom CTA label, define these as required fields for any inbox campaign targeting your custom implementation. ## Handle message actions (deeplink & URL) When a user taps a message, inspect `item.response.action` to determine what to do. The SDK does **not** fire deeplink callbacks automatically in a custom inbox — your app handles navigation directly. Always call `reportMessageClicked` so that the interaction is registered on the platform. **Swift** ```swift func userDidTap(_ item: XPInboxItem) {    // Register the click with the platform    XPush.reportMessageClicked(item.response.message, actionIdentifier: item.response.action.identifier)    if let deeplink = item.response.action.deeplink {        // Route through your app's navigation handler        // MyRouter.shared.navigate(to: deeplink)    } else if let urlString = item.response.action.url,              let url = URL(string: urlString) {        UIApplication.shared.open(url)    } else {        // No tap action configured — message is informational only    } } ``` **Objective-C** ```objectivec - (void)userDidTapInboxItem:(XPInboxItem *)item {    // Register the click with the platform    [XPush reportMessageClicked:item.response.message actionIdentifier:item.response.action.identifier];    if (item.response.action.deeplink) {        // Route through your app's navigation handler        // [[MyRouter shared] navigateTo:item.response.action.deeplink];    } else if (item.response.action.url) {        NSURL *url = [NSURL URLWithString:item.response.action.url];        [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];    } else {        // No tap action configured — message is informational only    } } ``` ## Report inbox message opened Call this when a message becomes visible to the user (e.g. scrolled into view in a feed) without a deliberate tap. **Swift** ```swift XPush.reportMessageOpened(item.response.message, actionIdentifier: item.response.action.identifier) ``` **Objective-C** ```objectivec [XPush reportMessageOpened:item.response.message actionIdentifier:item.response.action.identifier]; ``` ## Report inbox message clicked Call this when a user deliberately taps a message. This automatically also marks the message as opened. Do not call `reportMessageOpened` separately for tapped messages. **Swift** ```swift XPush.reportMessageClicked(item.response.message, actionIdentifier: item.response.action.identifier) ``` **Objective-C** ```objectivec [XPush reportMessageClicked:item.response.message actionIdentifier:item.response.action.identifier]; ``` ## Delete an inbox message **Swift** ```swift XPush.removeInboxMessage(item) { badge in    // reload inbox list    // update badge display } ``` **Objective-C** ```objectivec [XPush removeInboxMessage:item callback:^(NSInteger badge) {    // reload inbox list    // update badge display }]; ``` ## Get inbox badge number Returns the cached unread count — use this to display a badge without making a network request. **Swift** ```swift let badge: NSInteger = XPush.getInboxBadge() ``` **Objective-C** ```objectivec NSInteger badge = [XPush getInboxBadge]; ```