In SDK 9.0 we introduced a revamped In-App Messaging solution, In-App Automation, which adds extensive templating, server-backed guaranteed delivery, and automation capabilities. However, existing customers may want to continue sending In-App Message banners in push notifications. To support this, the SDK transparently converts these into the new automated message format and schedules them. In most cases this will work out of the box, requiring little to no migration. However there are a few edge cases where a custom approach might be helpful, and the SDK provides a mechanism for overriding or supplementing the message conversion process. This guide will cover a few of these cases, with example code for both iOS and Android.
Note:
For the sake of brevity and to avoid confusion, we will be referring to In-App Message banners sent in push notifications as legacy banners.
To Migrate or Not to Migrate
Prior to SDK 9.0, the standard behavior for legacy banners was to store an
incoming message as pending, and display it on the subsequent app foreground. In
cases where this behavior was undesirable, the developer had the option of
setting flags on the In-App Messaging manager class, such as
displayASAPEnabled
or autoDisplayEnabled
. Enabling the former would instead
cause messages to be displayed immediately on delivery, and disabling the latter
would block the SDK from automatically displaying the message, so that the app
could choose the right time and place, using the displayPendingMessage
method.
In SDK 9.0, legacy banners default to immediate display, and the flags and methods mentioned above have been removed, along with properties for setting default fonts, colors and so on. We believe the new defaults will cover the majority of use cases, but in the event this behavior needs customization, we've provided a way to intercept these banners as they are processed by the SDK, so that the app can either tweak or override their behavior.
Legacy In-App Message Banner Conversion: An Overview
When a legacy banner is received by the SDK, it's converted into the new,
automated message format and scheduled. These messages can be triggered in many
different ways, ranging from simple app state transitions, to screen tracking
events. Hooking into this flow is just a few lines of code. On iOS, it involves
registering a class implementing the UALegacyInAppMessageFactoryDelegate
protocol. The easiest way to do this is to create a new class and assign the
delegate in its init method:
@implementation MessageFactoryDelegate
+(instancetype)messageFactoryDelegate {
return [[MessageFactoryDelegate alloc] init];
}
-(instancetype)init {
self = [super init];
if (self) {
// Initialize delegate
[UAirship legacyInAppMessaging].factoryDelegate = self;
}
return self;
}
-(UAInAppMessageScheduleInfo *)scheduleInfoForMessage:(UALegacyInAppMessage *)message {
// Convert the legacy banner into an automated message here
}
@end
On Android it looks rather similar, but uses the extender pattern instead of a delegate callback:
UAirship.shared()
.getLegacyInAppMessageManager()
.setScheduleBuilderExtender(new LegacyInAppMessageManager.ScheduleInfoBuilderExtender() {
@NonNull
@Override
public InAppMessageScheduleInfo.Builder extend(Context context, InAppMessageScheduleInfo.Builder builder, LegacyInAppMessage legacyMessage) {
// Provide any changes to the builder here
return builder;
}
});
In both cases the basic idea is the same, which is that the application has the opportunity to decide how legacy banners are converted into automated message schedules, at any level of granularity. This could include scheduling new message types, such as modal and fullscreen, or adding additional media content. However for our purposes, we will be focusing on banners.
Postponing Message Display Until Next Foreground
Key to automated in-app messages are trigger types. Because these messages are
automated, the SDK is mostly responsible for deciding when the message will be
displayed, but the trigger type defines how that decision will be made. The
default type is active session, which will fire immediately if the app is in
the foreground, or wait until the next foreground if not. This directly
corresponds to enabling the displayAsap
flag in older SDKs. Reverting to the old
default behavior of always waiting until the next foreground is easy, and simply
involves swapping out the trigger for the foreground type.
On iOS, using our message factory delegate, we can call back into the
UALegacyInAppMessaging
class to convert the legacy banner into its default
automated message schedule, and with a few extra lines of code, reconfigure it
with the desired trigger:
- (UAInAppMessageScheduleInfo *)scheduleInfoForMessage:(UALegacyInAppMessage *)message {
UAInAppMessageScheduleInfo *defaultScheduleInfo = [[UAirship legacyInAppMessaging] scheduleInfoForMessage:message];
UAScheduleTrigger *trigger = [UAScheduleTrigger foregroundTriggerWithCount:1];
UAInAppMessageScheduleInfo *myScheduleInfo = [UAInAppMessageScheduleInfo scheduleInfoWithBuilderBlock:^(UAInAppMessageScheduleInfoBuilder * _Nonnull builder) {
builder.triggers = @[trigger];
builder.end = message.expiry;
builder.message = defaultScheduleInfo.message;
}];
return myScheduleInfo;
}
On Android the process is similar, but as outlined in the previous section we'll do this using an extender:
UAirship.shared()
.getLegacyInAppMessageManager()
.setScheduleBuilderExtender(new LegacyInAppMessageManager.ScheduleInfoBuilderExtender() {
@NonNull
@Override
public InAppMessageScheduleInfo.Builder extend(Context context, InAppMessageScheduleInfo.Builder builder, LegacyInAppMessage legacyMessage) {
Trigger foregroundTrigger = Triggers.newForegroundTriggerBuilder()
.setGoal(1)
.build();
builder.setTriggers(Arrays.asList(foregroundTrigger));
return builder;
}
});
Manually Displaying Pending Messages
Because In-App Messages are automated in SDK 9, and because the automation
pipeline supports multiple pending messages, the old displayPendingMessage
method no longer makes sense. However we can still achieve the same result by
tying messages to custom event triggers, and firing the associated custom event
when we want them to be displayed. This is very similar to approach shown above.
On iOS we can assign a custom event trigger as below:
- (UAInAppMessageScheduleInfo *)scheduleInfoForMessage:(UALegacyInAppMessage *)message {
UAInAppMessageScheduleInfo *defaultScheduleInfo = [[UAirship legacyInAppMessaging] scheduleInfoForMessage:message];
UAJSONValueMatcher *valueMatcher = [UAJSONValueMatcher matcherWhereStringEquals:@"displayLegacyBanner"];
UAJSONMatcher *jsonMatcher = [UAJSONMatcher matcherWithValueMatcher:valueMatcher key:UACustomEventNameKey];
UAJSONPredicate *predicate = [UAJSONPredicate predicateWithJSONMatcher:jsonMatcher];
UAScheduleTrigger *trigger = [UAScheduleTrigger customEventTriggerWithPredicate:predicate count:1];
UAInAppMessageScheduleInfo *myScheduleInfo = [UAInAppMessageScheduleInfo scheduleInfoWithBuilderBlock:^(UAInAppMessageScheduleInfoBuilder * _Nonnull builder) {
builder.triggers = @[trigger];
builder.end = message.expiry;
builder.message = defaultScheduleInfo.message;
}];
return myScheduleInfo;
}
And later, when it is time to fire the event:
UACustomEvent *event = [UACustomEvent eventWithName:@"displayLegacyBanner"];
[[UAirship analytics] addEvent:event];
On Android, as above we provide a custom event trigger in the extender:
UAirship.shared()
.getLegacyInAppMessageManager()
.setScheduleBuilderExtender(new LegacyInAppMessageManager.ScheduleInfoBuilderExtender() {
@NonNull
@Override
public InAppMessageScheduleInfo.Builder extend(Context context, InAppMessageScheduleInfo.Builder builder, LegacyInAppMessage legacyMessage) {
Trigger customEventTrigger = Triggers.newCustomEventTriggerBuilder()
.setEventName("displayLegacyBanner")
.setCountGoal(1)
.build();
builder.setTriggers(Arrays.asList(customEventTrigger));
return builder;
}
});
Likewise, when it is time to fire the event:
CustomEvent event = new CustomEvent.Builder("displayLegacyBanner").create();
event.track();
Of course, since these are custom events the developer has quite a bit of
flexibility in how to approach this. For instance, by using key/value pairs in
the legacy banner extras, different banners could be tied to different custom
events, and triggered individually. But the above approach is enough to
duplicate the behavior of displayPendingMessage
as found on older SDKs.