Skip to content

Commit 02b7060

Browse files
committed
Add location prompt handling for IAM clicks (#629)
* Add location prompt to IAM action * Show location prompt when available * Show multiple prompts when available * Add completionHandler to promptLocation * Add test for prompt and multiple prompts * Pause IAM display if user prompt displayed * Wait for app to be active for IAM evaluation
1 parent 8cb6d0e commit 02b7060

18 files changed

+295
-74
lines changed

iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@
9999
7A9173A2231971E5007848FA /* OneSignalReceiveReceiptsController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A9173A1231971E5007848FA /* OneSignalReceiveReceiptsController.m */; };
100100
7AA2848A2406FC6400C25D76 /* OSInAppMessageTag.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A1F2D8E2406EFC5007799A9 /* OSInAppMessageTag.m */; };
101101
7AA2848B2406FC6500C25D76 /* OSInAppMessageTag.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A1F2D8E2406EFC5007799A9 /* OSInAppMessageTag.m */; };
102+
7AD172382416D53B00A78B19 /* OSInAppMessageLocationPrompt.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AD172372416D53B00A78B19 /* OSInAppMessageLocationPrompt.m */; };
103+
7AD172392416D53B00A78B19 /* OSInAppMessageLocationPrompt.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AD172372416D53B00A78B19 /* OSInAppMessageLocationPrompt.m */; };
104+
7AD1723A2416D53B00A78B19 /* OSInAppMessageLocationPrompt.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AD172372416D53B00A78B19 /* OSInAppMessageLocationPrompt.m */; };
102105
7AD8DDE7234BD3BE00747A8A /* OneSignalUserDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AD8DDE6234BD3BE00747A8A /* OneSignalUserDefaults.m */; };
103106
7ADE379422E8B69C00263048 /* OneSignalOutcomeEventsController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ADE379322E8B69C00263048 /* OneSignalOutcomeEventsController.m */; };
104107
7ADE37AD22F2554400263048 /* OneSignalOutcomeEventsController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ADE379322E8B69C00263048 /* OneSignalOutcomeEventsController.m */; };
@@ -422,6 +425,8 @@
422425
7A880F302404AE7B0081F5E8 /* OSInAppMessagePushPrompt.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OSInAppMessagePushPrompt.m; sourceTree = "<group>"; };
423426
7A9173A1231971E5007848FA /* OneSignalReceiveReceiptsController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OneSignalReceiveReceiptsController.m; sourceTree = "<group>"; };
424427
7A9173A3231971F8007848FA /* OneSignalReceiveReceiptsController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OneSignalReceiveReceiptsController.h; sourceTree = "<group>"; };
428+
7AD172362416D52D00A78B19 /* OSInAppMessageLocationPrompt.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OSInAppMessageLocationPrompt.h; sourceTree = "<group>"; };
429+
7AD172372416D53B00A78B19 /* OSInAppMessageLocationPrompt.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OSInAppMessageLocationPrompt.m; sourceTree = "<group>"; };
425430
7AD8DDE6234BD3BE00747A8A /* OneSignalUserDefaults.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OneSignalUserDefaults.m; sourceTree = "<group>"; };
426431
7AD8DDE8234BD3CF00747A8A /* OneSignalUserDefaults.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OneSignalUserDefaults.h; sourceTree = "<group>"; };
427432
7ADE379322E8B69C00263048 /* OneSignalOutcomeEventsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OneSignalOutcomeEventsController.m; sourceTree = "<group>"; };
@@ -967,6 +972,8 @@
967972
7A880F2E2404AD010081F5E8 /* OSInAppMessagePrompt.h */,
968973
7A880F2F2404AD920081F5E8 /* OSInAppMessagePushPrompt.h */,
969974
7A880F302404AE7B0081F5E8 /* OSInAppMessagePushPrompt.m */,
975+
7AD172362416D52D00A78B19 /* OSInAppMessageLocationPrompt.h */,
976+
7AD172372416D53B00A78B19 /* OSInAppMessageLocationPrompt.m */,
970977
);
971978
name = Model;
972979
sourceTree = "<group>";
@@ -1237,6 +1244,7 @@
12371244
files = (
12381245
9D1BD968237A28FC00A064F7 /* OSUniqueOutcomeNotification.m in Sources */,
12391246
7A9173A2231971E5007848FA /* OneSignalReceiveReceiptsController.m in Sources */,
1247+
7AD172382416D53B00A78B19 /* OSInAppMessageLocationPrompt.m in Sources */,
12401248
9124120E1E73342200E41FD7 /* OneSignal.m in Sources */,
12411249
CACBAA97218A6243000ACAA5 /* OSMessagingController.m in Sources */,
12421250
CA36F35921C33A2500300C77 /* OSInAppMessageController.m in Sources */,
@@ -1310,6 +1318,7 @@
13101318
files = (
13111319
9D1BD969237A28FC00A064F7 /* OSUniqueOutcomeNotification.m in Sources */,
13121320
9124120F1E73342200E41FD7 /* OneSignal.m in Sources */,
1321+
7AD172392416D53B00A78B19 /* OSInAppMessageLocationPrompt.m in Sources */,
13131322
CACBAA98218A6243000ACAA5 /* OSMessagingController.m in Sources */,
13141323
CA36F35A21C33A2500300C77 /* OSInAppMessageController.m in Sources */,
13151324
CA08FC741FE99AFF004C445F /* OneSignalClient.m in Sources */,
@@ -1452,6 +1461,7 @@
14521461
CACBAAAA218A65AE000ACAA5 /* InAppMessagingTests.m in Sources */,
14531462
4529DEE71FA82CDC00CEAB1D /* UNUserNotificationCenterOverrider.m in Sources */,
14541463
4529DEDB1FA8284E00CEAB1D /* NSDataOverrider.m in Sources */,
1464+
7AD1723A2416D53B00A78B19 /* OSInAppMessageLocationPrompt.m in Sources */,
14551465
CA7FC8A221927229002C4FD9 /* OSDynamicTriggerController.m in Sources */,
14561466
9D3300F623145AF3000F0A83 /* OneSignalViewHelper.m in Sources */,
14571467
4529DEF31FA8440A00CEAB1D /* UIAlertViewOverrider.m in Sources */,

iOS_SDK/OneSignalSDK/Source/OSInAppMessageAction.m

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
#import "OSInAppMessageAction.h"
2929
#import "OSInAppMessagePushPrompt.h"
30+
#import "OSInAppMessageLocationPrompt.h"
3031

3132
@implementation OSInAppMessageAction
3233

@@ -94,8 +95,11 @@ + (instancetype)instanceWithJson:(NSDictionary *)json {
9495
NSArray<NSString *> *promptActionsStrings = json[@"prompts"];
9596

9697
for (NSString *prompt in promptActionsStrings) {
98+
// TODO: We should refactor this string handling to enums
9799
if ([prompt isEqualToString:@"push"]) {
98100
[promptActions addObject:[[OSInAppMessagePushPrompt alloc] init]];
101+
} else if ([prompt isEqualToString:@"location"]) {
102+
[promptActions addObject:[[OSInAppMessageLocationPrompt alloc] init]];
99103
}
100104
}
101105

@@ -105,8 +109,7 @@ + (instancetype)instanceWithJson:(NSDictionary *)json {
105109
return action;
106110
}
107111

108-
- (NSString *)description
109-
{
112+
- (NSString *)description {
110113
return [NSString stringWithFormat:@"OSInAppMessageAction outcome: %@ \ntag: %@ promptAction: %@", _outcomes, _tags, [_promptActions description]];
111114
}
112115

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Modified MIT License
3+
*
4+
* Copyright 2020 OneSignal
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* 1. The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* 2. All copies of substantial portions of the Software may only be used in connection
17+
* with services provided by OneSignal.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
* THE SOFTWARE.
26+
*/
27+
28+
#ifndef OSInAppMessageLocationPrompt_h
29+
#define OSInAppMessageLocationPrompt_h
30+
31+
#import "OSInAppMessagePrompt.h"
32+
33+
@interface OSInAppMessageLocationPrompt : NSObject <OSInAppMessagePrompt>
34+
35+
@property (nonatomic) BOOL hasPrompted;
36+
37+
@end
38+
39+
#endif /* OSInAppMessageLocationPrompt_h */
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* Modified MIT License
3+
*
4+
* Copyright 2020 OneSignal
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* 1. The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* 2. All copies of substantial portions of the Software may only be used in connection
17+
* with services provided by OneSignal.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
* THE SOFTWARE.
26+
*/
27+
28+
#import <Foundation/Foundation.h>
29+
#import "OSInAppMessageLocationPrompt.h"
30+
31+
@interface OneSignal ()
32+
33+
+ (void)promptLocation:(void (^)(BOOL accepted))completionHandler;
34+
35+
@end
36+
37+
@implementation OSInAppMessageLocationPrompt
38+
39+
- (instancetype)init
40+
{
41+
self = [super init];
42+
if (self) {
43+
_hasPrompted = NO;
44+
}
45+
return self;
46+
}
47+
48+
- (void)handlePrompt:(void (^)(BOOL accepted))completionHandler {
49+
[OneSignal promptLocation:completionHandler];
50+
}
51+
52+
- (NSString *)description {
53+
return [NSString stringWithFormat:@"OSInAppMessageLocationPrompt hasPrompted:%@", _hasPrompted ? @"YES" : @"NO"];
54+
}
55+
56+
@end

iOS_SDK/OneSignalSDK/Source/OSInAppMessageOutcome.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,8 @@ + (instancetype _Nullable)instancePreviewFromPayload:(OSNotificationPayload * _N
6565
return nil;
6666
}
6767

68+
- (NSString *)description {
69+
return [NSString stringWithFormat:@"OSInAppMessageOutcome name: %@\nweight: %@unique: %s\n", _name, _weight, _unique ? "YES" : "NO"];
70+
}
71+
6872
@end

iOS_SDK/OneSignalSDK/Source/OSInAppMessagePrompt.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434

3535
@protocol OSInAppMessagePrompt <NSObject>
3636

37-
@property (nonatomic) BOOL didAppear;
37+
@property (nonatomic) BOOL hasPrompted;
3838

3939
- (void)handlePrompt:(void (^)(BOOL accepted))completionHandler;
4040

iOS_SDK/OneSignalSDK/Source/OSInAppMessagePushPrompt.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
@interface OSInAppMessagePushPrompt : NSObject <OSInAppMessagePrompt>
3434

35-
@property (nonatomic) BOOL didAppear;
35+
@property (nonatomic) BOOL hasPrompted;
3636

3737
@end
3838

iOS_SDK/OneSignalSDK/Source/OSInAppMessagePushPrompt.m

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ - (instancetype)init
3434
{
3535
self = [super init];
3636
if (self) {
37-
_didAppear = NO;
37+
_hasPrompted = NO;
3838
}
3939
return self;
4040
}
@@ -43,9 +43,8 @@ - (void)handlePrompt:(void (^)(BOOL accepted))completionHandler {
4343
[OneSignal promptForPushNotificationsWithUserResponse:completionHandler fallbackToSettings:YES];
4444
}
4545

46-
- (NSString *)description
47-
{
48-
return [NSString stringWithFormat:@"OSInAppMessagePushPrompt didappear:%@", _didAppear ? @"YES" : @"NO"];
46+
- (NSString *)description {
47+
return [NSString stringWithFormat:@"OSInAppMessagePushPrompt hasPrompted:%@", _hasPrompted ? @"YES" : @"NO"];
4948
}
5049

5150
@end

iOS_SDK/OneSignalSDK/Source/OSMessagingController.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,13 @@
3333

3434
NS_ASSUME_NONNULL_BEGIN
3535

36-
@interface OSMessagingController : NSObject <OSInAppMessageViewControllerDelegate, OSTriggerControllerDelegate>
36+
@protocol OSMessagingControllerDelegate <NSObject>
37+
38+
- (void)onApplicationDidBecomeActive;
39+
40+
@end
41+
42+
@interface OSMessagingController : NSObject <OSInAppMessageViewControllerDelegate, OSTriggerControllerDelegate, OSMessagingControllerDelegate>
3743

3844
@property (class, readonly) BOOL isInAppMessagingPaused;
3945

iOS_SDK/OneSignalSDK/Source/OSMessagingController.m

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ @interface OSMessagingController ()
7272

7373
@property (nonatomic, nullable) NSObject<OSInAppMessagePrompt>*currentPromptAction;
7474

75+
@property (nonatomic) BOOL isAppInactive;
76+
7577
@end
7678

7779
@implementation OSMessagingController
@@ -138,6 +140,7 @@ - (instancetype)init {
138140
self.clickedClickIds = [[NSMutableSet alloc] initWithSet:[standardUserDefaults getSavedSetForKey:OS_IAM_CLICKED_SET_KEY defaultValue:nil]];
139141
self.impressionedInAppMessages = [[NSMutableSet alloc] initWithSet:[standardUserDefaults getSavedSetForKey:OS_IAM_IMPRESSIONED_SET_KEY defaultValue:nil]];
140142
self.currentPromptAction = nil;
143+
self.isAppInactive = NO;
141144
// BOOL that controls if in-app messaging is paused or not (false by default)
142145
[self setInAppMessagingPaused:false];
143146
}
@@ -212,7 +215,12 @@ - (void)presentInAppMessage:(OSInAppMessage *)message {
212215
// Return early if an IAM is already showing
213216
if (self.isInAppMessageShowing)
214217
return;
215-
218+
// Return early if the app is not active
219+
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateActive) {
220+
[OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:@"Pause IAMs display due to app inactivity"];
221+
_isAppInactive = YES;
222+
return;
223+
}
216224
[self displayMessage:message];
217225
};
218226
}
@@ -355,7 +363,6 @@ - (void)setDataForRedisplay:(OSInAppMessage *)message {
355363
[self.impressionedInAppMessages removeObject:message.messageId];
356364
[message clearClickIds];
357365
return;
358-
359366
}
360367
}
361368
}
@@ -439,7 +446,9 @@ - (void)messageViewControllerWasDismissed {
439446

440447
if (!_currentPromptAction) {
441448
[self evaluateMessageDisplayQueue];
442-
} // else do nothing prompt is handling the re-showing
449+
} else { //do nothing prompt is handling the re-showing
450+
[OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:@"Stop evaluateMessageDisplayQueue because prompt is currently displayed"];
451+
}
443452
}
444453
}
445454

@@ -488,26 +497,25 @@ - (void)persistInAppMessageForRedisplay:(OSInAppMessage *)message {
488497
[OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"persistInAppMessageForRedisplay: %@ \nredisplayedInAppMessages: %@", [message description], [_redisplayedInAppMessages description]]];
489498
}
490499

491-
- (void)handlePromptAction:(NSArray<NSObject<OSInAppMessagePrompt> *> *)promptActions {
500+
- (void)handlePromptActions:(NSArray<NSObject<OSInAppMessagePrompt> *> *)promptActions {
492501
for (NSObject<OSInAppMessagePrompt> *promptAction in promptActions) {
493-
if (![promptAction didAppear]) {
502+
// Don't show prompt twice
503+
if (!promptAction.hasPrompted) {
494504
_currentPromptAction = promptAction;
505+
break;
495506
}
496-
break;
497507
}
498508

499509
if (_currentPromptAction) {
500510
[OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"IAM prompt to handle: %@", [_currentPromptAction description]]];
501-
_currentPromptAction.didAppear = YES;
511+
_currentPromptAction.hasPrompted = YES;
502512
[_currentPromptAction handlePrompt:^(BOOL accepted) {
503513
_currentPromptAction = nil;
504-
// IAM dismissed by action
505-
if (!_viewController) {
506-
[OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:@"IAM with prompt dismissed from actionTaken"];
507-
[self evaluateMessageDisplayQueue];
508-
}
509-
//TODO: continue handling more than one prompt
514+
[self handlePromptActions:promptActions];
510515
}];
516+
} else if (!_viewController) { // IAM dismissed by action
517+
[OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:@"IAM with prompt dismissed from actionTaken"];
518+
[self evaluateMessageDisplayQueue];
511519
}
512520
}
513521

@@ -518,7 +526,7 @@ - (void)messageViewDidSelectAction:(OSInAppMessage *)message withAction:(OSInApp
518526
if (action.clickUrl)
519527
[self handleMessageActionWithURL:action];
520528

521-
[self handlePromptAction:action.promptActions];
529+
[self handlePromptActions:action.promptActions];
522530

523531
if (self.actionClickBlock)
524532
self.actionClickBlock(action);
@@ -624,6 +632,16 @@ - (void)triggerConditionChanged {
624632
[self evaluateMessages];
625633
}
626634

635+
#pragma mark OSMessagingControllerDelegate Methods
636+
- (void)onApplicationDidBecomeActive {
637+
// To avoid excesive message evaluation
638+
// we should re-evaluate all in-app messages only if it was paused by inactive
639+
if (_isAppInactive) {
640+
[OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:@"Evaluating messages due to inactive app"];
641+
_isAppInactive = NO;
642+
[self evaluateMessages];
643+
}
644+
}
627645
@end
628646

629647
@implementation DummyOSMessagingController

0 commit comments

Comments
 (0)