In-App Messages
The SDK shows in-app messages designed in the panel and reports when they are displayed, clicked and dismissed. How in-app messages work across platforms: In-App Messages.
Requirements
- The SDK installed and initialized, see Installation.
- The FCM token passed to the SDK, see Firebase and FCM Token.
- Background Modes → Remote notifications enabled in your app target.
application(_:didReceiveRemoteNotification:fetchCompletionHandler:)passing the notification tohandleNotification(_:).- A physical device for testing.
In-app messages do not need notification permission, as long as your app registers with APNs and passes the FCM token. If your app calls registerForRemoteNotifications() only after the user allows notifications, users who decline get no in-app messages.
Setup
In-app messages are delivered as silent (data only) pushes. They carry no alert, so iOS never passes them to UNUserNotificationCenterDelegate. They arrive only in the app delegate method below.
- Enable Background Modes → Remote notifications in Signing & Capabilities.
- Forward silent pushes to the SDK:
- Swift
- Objective-C
func application(_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
UserSDK.default?.handleNotification(userInfo)
completionHandler(.noData)
}
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[[UserSDK default] handleNotification:userInfo];
completionHandler(UIBackgroundFetchResultNoData);
}
The Swift concurrency form, application(_:didReceiveRemoteNotification:) async -> UIBackgroundFetchResult, works the same way. You can call handleNotification(_:) from any thread or task. It shows in-app messages and never counts a click.
With the app open, send an in-app message to your test contact. It appears on screen and is counted as displayed in the panel.
Handling
When a message is shown
- App in the foreground: the message is shown at once.
- App in the background or inactive: the SDK keeps the newest message and shows it the next time the app becomes active, if it arrived less than 6 hours earlier. A newer message replaces an older one, and each message is shown once.
logoutdiscards a waiting message. - App not running: iOS does not deliver silent pushes to an app the user swiped away, and may hold them back in Low Power Mode or with Background App Refresh turned off. Those messages are not shown.
The SDK shows each message in its own window above your app. Your navigation (a splash screen, full screen presentations, sheets, a replaced root view controller) does not block it. Screens you present while a message is visible are not blocked either: they appear under the message and are available after it closes.
In-app messages are shown in a separate window, and messages received in the background are shown on the next app open (within 6 hours). "Displayed" is reported only once the message is on screen, so displayed numbers in the panel can be lower than before the upgrade, and accurate.
Button taps
A tap on a button always reports a click and closes the message. If the button has a link, the SDK passes it to your inAppMessageClickHandler first, on the main thread:
- return
true: you handled the link, the SDK does nothing more, - return
false, or no handler: the SDK opens the link withUIApplication.open(_:), but only if iOS reports it can open it (canOpenURL). A custom URL scheme that is not listed in your app'sLSApplicationQueriesSchemesis not opened.
To open screens of your own app from a button, handle the link in inAppMessageClickHandler. See Opening App Screens.
UserSDK.default?.inAppMessageClickHandler = { url in
guard url.scheme == "myapp" || url.host == "www.example.com" else {
return false // let the SDK open it
}
// route to the matching screen
return true
}
Custom fonts
Without a fontResolver, every message uses the system font, also when the font set in the panel is built into iOS. To use the fonts from the panel, set fontResolver, and add fonts that are not built into iOS to your app. When the resolver returns nil, the SDK uses the system font.
Methods
inAppMessageClickHandler
Receives the link of every tapped button before the SDK opens it, on the main thread. Available in Swift only. Added in 1.3.0.
| Name | Type | Required | Description |
|---|---|---|---|
inAppMessageClickHandler | ((URL) -> Bool)? | No | Receives the button's URL, including custom schemes. Return true if you handled it. |
The click is reported to the panel in both cases.
UserSDK.default?.inAppMessageClickHandler = { url in
router.open(url)
return true
}
inAppNotificationClickDelegate, an object implementing InAppNotificationClickDelegate, is deprecated since 1.3.0 and stays until 2.0. It has the same meaning. When both are set, inAppMessageClickHandler is used.
fontResolver
Provides fonts for text in in-app messages. Set it after initializing the SDK.
| Name | Type | Required | Description |
|---|---|---|---|
fontResolver | FontResolving? | No | Implements resolveFontFor(name:size:) -> UIFont?. |
resolveFontFor(name: String, size: CGFloat) -> UIFont? receives the font name and size from the message. Return nil to use the system font. Without a resolver, the SDK always uses the system font.
- Swift
- Objective-C
final class AppFontResolver: NSObject, FontResolving {
func resolveFontFor(name: String, size: CGFloat) -> UIFont? {
UIFont(name: name, size: size)
}
}
UserSDK.default?.fontResolver = AppFontResolver()
@interface AppFontResolver : NSObject <FontResolving>
@end
@implementation AppFontResolver
- (UIFont *)resolveFontForName:(NSString *)name size:(CGFloat)size {
return [UIFont fontWithName:name size:size];
}
@end
[UserSDK default].fontResolver = [AppFontResolver new];
inAppAlertCloseButtonText
A static String property, default "Close". Has no effect. The close button is an icon without text. Kept for source compatibility.
Common errors
| Symptom | Cause | Fix |
|---|---|---|
| Push notifications work, in-app messages never appear | application(_:didReceiveRemoteNotification:fetchCompletionHandler:) is missing, is in a class iOS does not use as the app delegate, or does not call handleNotification(_:). | Add the method to the real app delegate. In SwiftUI, to the class attached with UIApplicationDelegateAdaptor. |
| In-app messages never appear, even in the foreground | The Remote notifications background mode is missing from the built Info.plist. | Enable Background Modes → Remote notifications. |
| Messages sent while the app was closed never appear | iOS does not deliver silent pushes to an app the user swiped away. | Expected. Messages sent while the app is in the background are shown on the next open, within 6 hours. |
| Button does nothing except closing the message | The link uses a custom scheme the SDK cannot open, and no handler took it. | Handle the link in inAppMessageClickHandler. |
| Text uses the wrong font | No fontResolver set, the font is not in the app, or fontResolver returns nil. | Set fontResolver, add the font to the app and return it from fontResolver. |
The app crashed in CoreAutoLayout, or messages disappeared, when notifications were handled in a Task or on a background queue | Versions before 1.2.0 required the main thread. | Update to 1.2.0. |
More cases: iOS Troubleshooting.