Skip to content

Commit faf65b0

Browse files
authored
Action manager fixes (#507)
1 parent 11b874c commit faf65b0

File tree

10 files changed

+102
-29
lines changed

10 files changed

+102
-29
lines changed

LeanplumSDK/LeanplumSDK/Classes/Internal/Leanplum.m

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2564,12 +2564,6 @@ + (LPSecuredVars *)securedVars
25642564
return [[LPVarCache sharedCache] securedVars];;
25652565
}
25662566

2567-
2568-
/**
2569-
* Checks if message should be suppressed based on the local IAM caps.
2570-
* @param context The message context to check.
2571-
* @return True if message should be suppressed, false otherwise.
2572-
*/
25732567
+ (BOOL)shouldSuppressMessage:(LPActionContext *)context
25742568
{
25752569
if([LP_PUSH_NOTIFICATION_ACTION isEqualToString:[context actionName]]) {

LeanplumSDK/LeanplumSDK/Classes/Leanplum.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,13 @@ NS_SWIFT_NAME(start(userId:attributes:completion:));
371371
dismissHandler:(nullable LeanplumActionBlock)dismissHandler
372372
NS_SWIFT_NAME(defineAction(name:kind:args:options:present:dismiss:));
373373

374+
/**
375+
* Checks if message should be suppressed based on the local IAM caps.
376+
* @param context The message context to check.
377+
* @return True if message should be suppressed, false otherwise.
378+
*/
379+
+ (BOOL)shouldSuppressMessage:(LPActionContext *)context;
380+
374381
+ (void)applicationDidFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id> *)launchOptions;
375382
+ (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)token;
376383
+ (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error;

LeanplumSDK/LeanplumSDK/Classes/MessageTemplates/LPAppRatingMessageTemplate.m

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,24 @@ +(void)defineAction
2323

2424
[appRatingMessageTemplate appStorePrompt];
2525

26+
/**
27+
* There is no completion handler for the requestReview.
28+
* No information is returned if the prompt has been shown or not.
29+
* It could be possible to check if a window is presented by comparing the windows count but this is not reliable.
30+
*
31+
* Action is marked as dismissed so the queue can continue executing.
32+
* The app request is shown on a separate window,
33+
* so even if an alert message is presented while the App Review is present,
34+
* it will show underneath and not break the UI.
35+
*
36+
* If this behavior is undesired, then dispatch after a delay,
37+
* to provide some time to the user to rate the app, then dismiss the action.
38+
*/
39+
40+
dispatch_async(dispatch_get_main_queue(), ^{
41+
[context actionDismissed];
42+
});
43+
2644
return YES;
2745
} @catch (NSException *exception) {
2846
LOG_LP_MESSAGE_EXCEPTION;
@@ -38,7 +56,21 @@ - (void)appStorePrompt
3856
{
3957
dispatch_async(dispatch_get_main_queue(), ^{
4058
if (NSClassFromString(@"SKStoreReviewController")) {
41-
if (@available(iOS 10.3, *)) {
59+
if (@available(iOS 14.0, *)) {
60+
// Find active scene
61+
__block UIScene *scene;
62+
[[[UIApplication sharedApplication] connectedScenes] enumerateObjectsUsingBlock:^(UIScene * _Nonnull obj, BOOL * _Nonnull stop) {
63+
if (obj.activationState == UISceneActivationStateForegroundActive) {
64+
scene = obj;
65+
*stop = YES;
66+
}
67+
}];
68+
// Present using scene
69+
UIWindowScene *windowScene = (UIWindowScene*)scene;
70+
if (windowScene) {
71+
[SKStoreReviewController requestReviewInScene:windowScene];
72+
}
73+
} else if (@available(iOS 10.3, *)) {
4274
[SKStoreReviewController requestReview];
4375
}
4476
}

LeanplumSDK/LeanplumSDK/Classes/MessageTemplates/LPOpenUrlMessageTemplate.m

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,29 @@ +(void)defineAction
2424
@try {
2525
LPOpenUrlMessageTemplate *template = [[LPOpenUrlMessageTemplate alloc] init];
2626
template.context = context;
27-
[template openURL];
28-
29-
[context actionDismissed];
3027

28+
[template openURLWithCompletion:^(BOOL success) {
29+
/**
30+
* When the action is dismissed, the ActionManager queue continues to perform actions.
31+
* If the URL opens an external app or browser, there is a delay
32+
* before UIApplication.willResignActiveNotification or UIScene.willDeactivateNotification are executed.
33+
* This delay causes next actions in the queue to execute before the app resigns or application state changes.
34+
* If there are other OpenURL actions in the queue, those actions will be perfomed by the ActionManager
35+
* until the application resigns active (which pauses the main queue respectively the ActionManager queue).
36+
* However, the application:openURL will fail to open them hence they will not be presented.
37+
*
38+
* This happens in the edge case where there are multiple Open URL actions executed one after another, likely when the queue was paused and actions were opened.
39+
* Since real use case implications should be extremely minimal, the implementation is left as is.
40+
* If a workaround should be added, dispatch the actionDismissed after a delay - pausing the queue when app willResign will not work due to the delay explained above.
41+
* dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
42+
* [context actionDismissed];
43+
* });
44+
*/
45+
dispatch_async(dispatch_get_main_queue(), ^{
46+
[context actionDismissed];
47+
});
48+
}];
49+
3150
return YES;
3251
}
3352
@catch (NSException *exception) {
@@ -40,14 +59,13 @@ +(void)defineAction
4059
}];
4160
}
4261

43-
- (void) openURL
62+
- (void)openURLWithCompletion:(void (^ __nonnull)(BOOL success))completion
4463
{
4564
dispatch_async(dispatch_get_main_queue(), ^{
4665
NSString *encodedURLString = [self urlEncodedStringFromString:[self.context stringNamed:LPMT_ARG_URL]];
4766
NSURL *url = [NSURL URLWithString: encodedURLString];
48-
[LPUtils openURL:url];
67+
[LPUtils openURL:url completionHandler:completion];
4968
});
50-
5169
}
5270

5371
- (NSString *)urlEncodedStringFromString:(NSString *)urlString {

LeanplumSDK/LeanplumSDK/Classes/Utilities/LPUtils.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,12 @@
7070
/**
7171
* Open URLs from SDK
7272
*/
73-
+ (void)openURL:(NSURL*)url;
73+
+ (void)openURL:(NSURL *)url;
74+
75+
/**
76+
* Open URLs from SDK and calls the completionHandler
77+
*/
78+
+ (void)openURL:(NSURL *)url completionHandler:(void (^ __nullable)(BOOL success))completion;
7479

7580
/**
7681
* Checks if given value is a NSNumber with bool value

LeanplumSDK/LeanplumSDK/Classes/Utilities/LPUtils.m

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,13 +111,21 @@ + (NSBundle *)leanplumBundle
111111
}
112112

113113
+ (void)openURL:(NSURL *)url
114+
{
115+
[self openURL:url completionHandler:nil];
116+
}
117+
118+
+ (void)openURL:(NSURL *)url completionHandler:(void (^ __nullable)(BOOL success))completion
114119
{
115120
if (@available(iOS 10.0, *)) {
116-
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
121+
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:completion];
117122
} else {
118123
#pragma clang diagnostic push
119124
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
120125
[[UIApplication sharedApplication] openURL:url];
126+
if (completion) {
127+
completion(YES);
128+
}
121129
#pragma clang diagnostic pop
122130
}
123131
}

LeanplumSDK/LeanplumSDK/ClassesSwift/Actions/ActionManager+Definition.swift

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,4 @@ extension ActionManager {
1616
@objc public func definition(withName name: String) -> ActionDefinition? {
1717
return self.definitions.first(where: { $0.name == name })
1818
}
19-
20-
func getActionDefinitionType(name: String) -> UInt {
21-
let definition = definition(withName: name)
22-
if let definition = definition {
23-
return definition.kind.rawValue
24-
}
25-
return 0
26-
}
2719
}

LeanplumSDK/LeanplumSDK/ClassesSwift/Actions/ActionManager+Executor.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ extension ActionManager {
3737
}
3838

3939
Log.debug("[ActionManager]: running action with name: \(action.context).")
40+
41+
if action.type == .single,
42+
Leanplum.shouldSuppressMessage(action.context) {
43+
Log.info("[ActionManager]: local IAM caps reached, suppressing \(action.context).")
44+
state.currentAction = nil
45+
performAvailableActions()
46+
return
47+
}
4048

4149
// decide if we are going to display the message
4250
// by calling delegate and let it decide what are we supposed to do

LeanplumSDK/LeanplumSDK/ClassesSwift/Actions/ActionManager+Tracking.swift

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,31 @@ import Foundation
1010
extension ActionManager {
1111
func recordImpression(action: Action) {
1212
typealias Kind = Leanplum.ActionKind
13-
if action.type == .chained {
13+
14+
switch action.type {
15+
16+
case .single:
17+
LPActionTriggerManager.shared().recordMessageImpression(action.context.messageId)
18+
19+
case .chained:
1420
// We do not want to count occurrences for action kind, because in multi message
1521
// campaigns the Open URL action is not a message. Also if the user has defined
1622
// actions of type Action we do not want to count them.
23+
guard let actionKind = definition(withName: action.context.name)?.kind else {
24+
break
25+
}
1726

18-
let actionKind: Kind = .init(rawValue: getActionDefinitionType(name: action.context.name))
1927
switch actionKind {
2028
case .action:
2129
LPActionTriggerManager.shared().recordChainedActionImpression(action.context.messageId)
22-
case .message:
30+
case .message, [.action, .message]:
2331
LPActionTriggerManager.shared().recordMessageImpression(action.context.messageId)
2432
default:
2533
break
2634
}
27-
} else {
28-
LPActionTriggerManager.shared().recordMessageImpression(action.context.messageId)
35+
36+
case .embedded:
37+
break
2938
}
3039
}
3140
}

LeanplumSDK/LeanplumSDK/ClassesSwift/Actions/ActionManager.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ import Foundation
5353
NotificationCenter
5454
.default
5555
.addObserver(forName: UIApplication.didBecomeActiveNotification,
56-
object: self,
56+
object: nil,
5757
queue: .main) { [weak self] _ in
5858
guard let `self` = self else { return }
5959
if self.configuration.resumeOnEnterForeground {

0 commit comments

Comments
 (0)