Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Action manager fixes #507

Merged
merged 6 commits into from
Aug 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions LeanplumSDK/LeanplumSDK/Classes/Internal/Leanplum.m
Original file line number Diff line number Diff line change
Expand Up @@ -2564,12 +2564,6 @@ + (LPSecuredVars *)securedVars
return [[LPVarCache sharedCache] securedVars];;
}


/**
* Checks if message should be suppressed based on the local IAM caps.
* @param context The message context to check.
* @return True if message should be suppressed, false otherwise.
*/
+ (BOOL)shouldSuppressMessage:(LPActionContext *)context
{
if([LP_PUSH_NOTIFICATION_ACTION isEqualToString:[context actionName]]) {
Expand Down
7 changes: 7 additions & 0 deletions LeanplumSDK/LeanplumSDK/Classes/Leanplum.h
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,13 @@ NS_SWIFT_NAME(start(userId:attributes:completion:));
dismissHandler:(nullable LeanplumActionBlock)dismissHandler
NS_SWIFT_NAME(defineAction(name:kind:args:options:present:dismiss:));

/**
* Checks if message should be suppressed based on the local IAM caps.
* @param context The message context to check.
* @return True if message should be suppressed, false otherwise.
*/
+ (BOOL)shouldSuppressMessage:(LPActionContext *)context;

+ (void)applicationDidFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id> *)launchOptions;
+ (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)token;
+ (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,24 @@ +(void)defineAction

[appRatingMessageTemplate appStorePrompt];

/**
* There is no completion handler for the requestReview.
* No information is returned if the prompt has been shown or not.
* It could be possible to check if a window is presented by comparing the windows count but this is not reliable.
*
* Action is marked as dismissed so the queue can continue executing.
* The app request is shown on a separate window,
* so even if an alert message is presented while the App Review is present,
* it will show underneath and not break the UI.
*
* If this behavior is undesired, then dispatch after a delay,
* to provide some time to the user to rate the app, then dismiss the action.
*/

dispatch_async(dispatch_get_main_queue(), ^{
[context actionDismissed];
});

return YES;
} @catch (NSException *exception) {
LOG_LP_MESSAGE_EXCEPTION;
Expand All @@ -38,7 +56,21 @@ - (void)appStorePrompt
{
dispatch_async(dispatch_get_main_queue(), ^{
if (NSClassFromString(@"SKStoreReviewController")) {
if (@available(iOS 10.3, *)) {
if (@available(iOS 14.0, *)) {
// Find active scene
__block UIScene *scene;
[[[UIApplication sharedApplication] connectedScenes] enumerateObjectsUsingBlock:^(UIScene * _Nonnull obj, BOOL * _Nonnull stop) {
if (obj.activationState == UISceneActivationStateForegroundActive) {
scene = obj;
*stop = YES;
}
}];
// Present using scene
UIWindowScene *windowScene = (UIWindowScene*)scene;
if (windowScene) {
[SKStoreReviewController requestReviewInScene:windowScene];
}
} else if (@available(iOS 10.3, *)) {
[SKStoreReviewController requestReview];
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,29 @@ +(void)defineAction
@try {
LPOpenUrlMessageTemplate *template = [[LPOpenUrlMessageTemplate alloc] init];
template.context = context;
[template openURL];

[context actionDismissed];

[template openURLWithCompletion:^(BOOL success) {
/**
* When the action is dismissed, the ActionManager queue continues to perform actions.
* If the URL opens an external app or browser, there is a delay
* before UIApplication.willResignActiveNotification or UIScene.willDeactivateNotification are executed.
* This delay causes next actions in the queue to execute before the app resigns or application state changes.
* If there are other OpenURL actions in the queue, those actions will be perfomed by the ActionManager
* until the application resigns active (which pauses the main queue respectively the ActionManager queue).
* However, the application:openURL will fail to open them hence they will not be presented.
*
* 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.
* Since real use case implications should be extremely minimal, the implementation is left as is.
* 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.
* dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
* [context actionDismissed];
* });
*/
dispatch_async(dispatch_get_main_queue(), ^{
[context actionDismissed];
});
}];

return YES;
}
@catch (NSException *exception) {
Expand All @@ -40,14 +59,13 @@ +(void)defineAction
}];
}

- (void) openURL
- (void)openURLWithCompletion:(void (^ __nonnull)(BOOL success))completion
{
dispatch_async(dispatch_get_main_queue(), ^{
NSString *encodedURLString = [self urlEncodedStringFromString:[self.context stringNamed:LPMT_ARG_URL]];
NSURL *url = [NSURL URLWithString: encodedURLString];
[LPUtils openURL:url];
[LPUtils openURL:url completionHandler:completion];
});

}

- (NSString *)urlEncodedStringFromString:(NSString *)urlString {
Expand Down
7 changes: 6 additions & 1 deletion LeanplumSDK/LeanplumSDK/Classes/Utilities/LPUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,12 @@
/**
* Open URLs from SDK
*/
+ (void)openURL:(NSURL*)url;
+ (void)openURL:(NSURL *)url;

/**
* Open URLs from SDK and calls the completionHandler
*/
+ (void)openURL:(NSURL *)url completionHandler:(void (^ __nullable)(BOOL success))completion;

/**
* Checks if given value is a NSNumber with bool value
Expand Down
10 changes: 9 additions & 1 deletion LeanplumSDK/LeanplumSDK/Classes/Utilities/LPUtils.m
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,21 @@ + (NSBundle *)leanplumBundle
}

+ (void)openURL:(NSURL *)url
{
[self openURL:url completionHandler:nil];
}

+ (void)openURL:(NSURL *)url completionHandler:(void (^ __nullable)(BOOL success))completion
{
if (@available(iOS 10.0, *)) {
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:completion];
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[[UIApplication sharedApplication] openURL:url];
if (completion) {
completion(YES);
}
#pragma clang diagnostic pop
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,4 @@ extension ActionManager {
@objc public func definition(withName name: String) -> ActionDefinition? {
return self.definitions.first(where: { $0.name == name })
}

func getActionDefinitionType(name: String) -> UInt {
let definition = definition(withName: name)
if let definition = definition {
return definition.kind.rawValue
}
return 0
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ extension ActionManager {
}

Log.debug("[ActionManager]: running action with name: \(action.context).")

if action.type == .single,
Leanplum.shouldSuppressMessage(action.context) {
Log.info("[ActionManager]: local IAM caps reached, suppressing \(action.context).")
state.currentAction = nil
performAvailableActions()
return
}

// decide if we are going to display the message
// by calling delegate and let it decide what are we supposed to do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,31 @@ import Foundation
extension ActionManager {
func recordImpression(action: Action) {
typealias Kind = Leanplum.ActionKind
if action.type == .chained {

switch action.type {

case .single:
LPActionTriggerManager.shared().recordMessageImpression(action.context.messageId)

case .chained:
// We do not want to count occurrences for action kind, because in multi message
// campaigns the Open URL action is not a message. Also if the user has defined
// actions of type Action we do not want to count them.
guard let actionKind = definition(withName: action.context.name)?.kind else {
break
}

let actionKind: Kind = .init(rawValue: getActionDefinitionType(name: action.context.name))
switch actionKind {
case .action:
LPActionTriggerManager.shared().recordChainedActionImpression(action.context.messageId)
case .message:
case .message, [.action, .message]:
LPActionTriggerManager.shared().recordMessageImpression(action.context.messageId)
default:
break
}
} else {
LPActionTriggerManager.shared().recordMessageImpression(action.context.messageId)

case .embedded:
break
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import Foundation
NotificationCenter
.default
.addObserver(forName: UIApplication.didBecomeActiveNotification,
object: self,
object: nil,
queue: .main) { [weak self] _ in
guard let `self` = self else { return }
if self.configuration.resumeOnEnterForeground {
Expand Down