Skip to content

Commit 16fbcf6

Browse files
authored
Fix bug where app launch messages weren't appearing on iOS (#3237)
* Fix bug where messages with `app_launch` as a trigger weren't being parsed correctly * Add a call to display executor check for app launch messages upon completion of initial app launch message fetch. * Verify that the check for app launch messages gets called on initial app launch fetch * Whitespace fix * Fix duplicated error codes in checkAndDisplayNextAppLaunchMessage * Refactor out logic for looking into app launch/foreground trigger messages to share more code * Whitespace fix
1 parent a1fc9b2 commit 16fbcf6

14 files changed

+114
-22
lines changed

Firebase/InAppMessaging/Data/FIRIAMFetchResponseParser.m

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,12 @@ - (instancetype)initWithTimeFetcher:(id<FIRIAMTimeFetcher>)timeFetcher {
101101
NSMutableArray<FIRIAMDisplayTriggerDefinition *> *triggers = [[NSMutableArray alloc] init];
102102

103103
for (NSDictionary *nextTriggerCondition in triggerConditions) {
104+
// Handle app_launch and on_foreground cases.
104105
if (nextTriggerCondition[@"fiamTrigger"]) {
105106
if ([nextTriggerCondition[@"fiamTrigger"] isEqualToString:@"ON_FOREGROUND"]) {
106107
[triggers addObject:[[FIRIAMDisplayTriggerDefinition alloc] initForAppForegroundTrigger]];
108+
} else if ([nextTriggerCondition[@"fiamTrigger"] isEqualToString:@"APP_LAUNCH"]) {
109+
[triggers addObject:[[FIRIAMDisplayTriggerDefinition alloc] initForAppLaunchTrigger]];
107110
}
108111
} else if ([nextTriggerCondition[@"event"] isKindOfClass:[NSDictionary class]]) {
109112
NSDictionary *triggeringEvent = (NSDictionary *)nextTriggerCondition[@"event"];

Firebase/InAppMessaging/Data/FIRIAMMessageDefinition.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
#import <Foundation/Foundation.h>
1717

18+
#import "FIRIAMDisplayTriggerDefinition.h"
1819
#import "FIRIAMMessageRenderData.h"
1920

2021
@class FIRIAMDisplayTriggerDefinition;
@@ -52,8 +53,9 @@ NS_ASSUME_NONNULL_BEGIN
5253
- (BOOL)messageHasExpired;
5354
- (BOOL)messageHasStarted;
5455

55-
// should this message be rendered when the app gets foregrounded?
56-
- (BOOL)messageRenderedOnAppForegroundEvent;
56+
// should this message be rendered given the FIAM trigger type? only use this method for app launch
57+
// and foreground trigger, use messageRenderedOnAnalyticsEvent: for analytics triggers
58+
- (BOOL)messageRenderedOnTrigger:(FIRIAMRenderTrigger)trigger;
5759
// should this message be rendered when a given analytics event is fired?
5860
- (BOOL)messageRenderedOnAnalyticsEvent:(NSString *)eventName;
5961
@end

Firebase/InAppMessaging/Data/FIRIAMMessageDefinition.m

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
*/
1616

1717
#import "FIRIAMMessageDefinition.h"
18-
#import "FIRIAMDisplayTriggerDefinition.h"
1918

2019
@implementation FIRIAMMessageRenderData
2120

@@ -60,9 +59,9 @@ - (BOOL)messageHasExpired {
6059
return self.endTime < [[NSDate date] timeIntervalSince1970];
6160
}
6261

63-
- (BOOL)messageRenderedOnAppForegroundEvent {
62+
- (BOOL)messageRenderedOnTrigger:(FIRIAMRenderTrigger)trigger {
6463
for (FIRIAMDisplayTriggerDefinition *nextTrigger in self.renderTriggers) {
65-
if (nextTrigger.triggerType == FIRIAMRenderTriggerOnAppForeground) {
64+
if (nextTrigger.triggerType == trigger) {
6665
return YES;
6766
}
6867
}

Firebase/InAppMessaging/DisplayTrigger/FIRIAMDisplayTriggerDefinition.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#import <Foundation/Foundation.h>
1818

1919
typedef NS_ENUM(NSInteger, FIRIAMRenderTrigger) {
20+
FIRIAMRenderTriggerOnAppLaunch,
2021
FIRIAMRenderTriggerOnAppForeground,
2122
FIRIAMRenderTriggerOnFirebaseAnalyticsEvent
2223
};
@@ -28,6 +29,7 @@ NS_ASSUME_NONNULL_BEGIN
2829
// applicable only when triggerType == FIRIAMRenderTriggerOnFirebaseAnalyticsEvent
2930
@property(nonatomic, copy, nullable, readonly) NSString *firebaseEventName;
3031

32+
- (instancetype)initForAppLaunchTrigger;
3133
- (instancetype)initForAppForegroundTrigger;
3234
- (instancetype)initWithFirebaseAnalyticEvent:(NSString *)title;
3335
@end

Firebase/InAppMessaging/DisplayTrigger/FIRIAMDisplayTriggerDefinition.m

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@
1717
#import "FIRIAMDisplayTriggerDefinition.h"
1818

1919
@implementation FIRIAMDisplayTriggerDefinition
20+
21+
- (instancetype)initForAppLaunchTrigger {
22+
if (self = [super init]) {
23+
_triggerType = FIRIAMRenderTriggerOnAppLaunch;
24+
}
25+
return self;
26+
}
27+
2028
- (instancetype)initForAppForegroundTrigger {
2129
if (self = [super init]) {
2230
_triggerType = FIRIAMRenderTriggerOnAppForeground;

Firebase/InAppMessaging/Flows/FIRIAMDisplayExecutor.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ NS_ASSUME_NONNULL_BEGIN
4949
activityLogger:(FIRIAMActivityLogger *)activityLogger
5050
analyticsEventLogger:(id<FIRIAMAnalyticsEventLogger>)analyticsEventLogger;
5151

52+
// Check and display next in-app message eligible for app launch trigger
53+
- (void)checkAndDisplayNextAppLaunchMessage;
5254
// Check and display next in-app message eligible for app open trigger
5355
- (void)checkAndDisplayNextAppForegroundMessage;
5456
// Check and display next in-app message eligible for analytics event trigger with given event name.

Firebase/InAppMessaging/Flows/FIRIAMDisplayExecutor.m

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,49 @@ - (BOOL)enoughIntervalFromLastDisplay {
604604
return intervalFromLastDisplayInSeconds >= self.setting.displayMinIntervalInMinutes * 60.0;
605605
}
606606

607+
- (void)checkAndDisplayNextAppLaunchMessage {
608+
// synchronizing on self so that we won't potentially enter the render flow from two
609+
// threads.
610+
@synchronized(self) {
611+
if (!self.messageDisplayComponent) {
612+
FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM400028",
613+
@"Message display component is not present yet. No display should happen.");
614+
return;
615+
}
616+
617+
if (self.suppressMessageDisplay) {
618+
FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM400029",
619+
@"Message display is being suppressed. No regular message rendering.");
620+
return;
621+
}
622+
623+
if (self.isMsgBeingDisplayed) {
624+
FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM400030",
625+
@"An in-app message display is in progress, do not over-display on top of it.");
626+
return;
627+
}
628+
629+
if ([self.messageCache hasTestMessage] || [self enoughIntervalFromLastDisplay]) {
630+
// We can display test messages anytime or display regular messages when
631+
// the display time interval has been reached
632+
FIRIAMMessageDefinition *nextAppLaunchMessage = [self.messageCache nextOnAppLaunchDisplayMsg];
633+
634+
if (nextAppLaunchMessage) {
635+
[self displayForMessage:nextAppLaunchMessage
636+
triggerType:FIRInAppMessagingDisplayTriggerTypeOnAnalyticsEvent];
637+
self.lastDisplayTime = [self.timeFetcher currentTimestampInSeconds];
638+
} else {
639+
FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM400040",
640+
@"No appropriate in-app message detected for display.");
641+
}
642+
} else {
643+
FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM400041",
644+
@"Minimal display interval of %lf seconds has not been reached yet.",
645+
self.setting.displayMinIntervalInMinutes * 60.0);
646+
}
647+
}
648+
}
649+
607650
- (void)checkAndDisplayNextAppForegroundMessage {
608651
// synchronizing on self so that we won't potentially enter the render flow from two
609652
// threads: example like showing analytics triggered message and a regular app open

Firebase/InAppMessaging/Flows/FIRIAMFetchFlow.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ typedef void (^FIRIAMFetchMessageCompletionHandler)(
5454

5555
// Triggers a potential fetch of in-app messaging from the source. It would check and respect the
5656
// the fetchMinIntervalInMinutes defined in setting
57-
- (void)checkAndFetch;
57+
- (void)checkAndFetchForInitialAppLaunch:(BOOL)forInitialAppLaunch;
58+
5859
@end
5960
NS_ASSUME_NONNULL_END

Firebase/InAppMessaging/Flows/FIRIAMFetchFlow.m

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#import "FIRCore+InAppMessaging.h"
2020
#import "FIRIAMClearcutLogger.h"
2121
#import "FIRIAMFetchFlow.h"
22+
#import "FIRIAMRuntimeManager.h"
2223

2324
@implementation FIRIAMFetchSetting
2425
@end
@@ -124,7 +125,7 @@ - (void)handleSuccessullyFetchedMessages:(NSArray<FIRIAMMessageDefinition *> *)m
124125
nextFetchWaitTime:fetchWaitTime];
125126
}
126127

127-
- (void)checkAndFetch {
128+
- (void)checkAndFetchForInitialAppLaunch:(BOOL)forInitialAppLaunch {
128129
NSTimeInterval intervalFromLastFetchInSeconds =
129130
[self.timeFetcher currentTimestampInSeconds] - self.fetchBookKeeper.lastFetchTime;
130131

@@ -229,6 +230,10 @@ - (void)checkAndFetch {
229230
[self handleSuccessullyFetchedMessages:messages
230231
withFetchWaitTime:nextFetchWaitTime
231232
requestImpressions:impressions];
233+
234+
if (forInitialAppLaunch) {
235+
[self checkForAppLaunchMessage];
236+
}
232237
}
233238
// Send this regardless whether fetch is successful or not.
234239
[self sendFetchIsDoneNotification];
@@ -250,4 +255,9 @@ - (void)checkAndFetch {
250255
[self.activityLogger addLogRecord:record];
251256
}
252257
}
258+
259+
- (void)checkForAppLaunchMessage {
260+
[[FIRIAMRuntimeManager getSDKRuntimeInstance]
261+
.displayExecutor checkAndDisplayNextAppLaunchMessage];
262+
}
253263
@end

Firebase/InAppMessaging/Flows/FIRIAMFetchOnAppForegroundFlow.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ - (void)appWillEnterForeground:(UIApplication *)application {
3535
// to a concurrent global queue instead of serial queue since app open event won't happen at
3636
// fast speed to cause race conditions
3737
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul), ^{
38-
[self checkAndFetch];
38+
[self checkAndFetchForInitialAppLaunch:NO];
3939
});
4040
}
4141

0 commit comments

Comments
 (0)