Skip to content

Commit 34bd301

Browse files
authored
Make all actions async to insert chained actions in correct order (#561)
* Make all actions async to insert chained actions in correct order * Add logging * Use action queue * Fix chained open action
1 parent 0bcfe89 commit 34bd301

File tree

8 files changed

+103
-25
lines changed

8 files changed

+103
-25
lines changed

LeanplumSDK/LeanplumSDK.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@
226226
6A37A89D28EF748800F4339F /* Dictionary+MapKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A37A89B28EF748800F4339F /* Dictionary+MapKeys.swift */; };
227227
6A37A8A028EF74E900F4339F /* CTWrapper+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A37A89F28EF74E900F4339F /* CTWrapper+Utilities.swift */; };
228228
6A37A8A128EF74E900F4339F /* CTWrapper+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A37A89F28EF74E900F4339F /* CTWrapper+Utilities.swift */; };
229+
6A3B84F529F075A500DE5CE0 /* Thread+Name.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A3B84F429F075A500DE5CE0 /* Thread+Name.swift */; };
230+
6A3B84F629F075A500DE5CE0 /* Thread+Name.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A3B84F429F075A500DE5CE0 /* Thread+Name.swift */; };
229231
6A714AF326F8B317004A34A9 /* LPConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 075AADDA26847EC4007CA1BD /* LPConstants.h */; settings = {ATTRIBUTES = (Public, ); }; };
230232
6A714AF426F8B317004A34A9 /* LPActionTriggerManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 075AADC726847EC3007CA1BD /* LPActionTriggerManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
231233
6A714AF526F8B317004A34A9 /* LPWebInterstitialViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 075AAD7426847EC3007CA1BD /* LPWebInterstitialViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -807,6 +809,7 @@
807809
6A37A89828EF738200F4339F /* MigrationManager+API.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MigrationManager+API.swift"; sourceTree = "<group>"; };
808810
6A37A89B28EF748800F4339F /* Dictionary+MapKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dictionary+MapKeys.swift"; sourceTree = "<group>"; };
809811
6A37A89F28EF74E900F4339F /* CTWrapper+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CTWrapper+Utilities.swift"; sourceTree = "<group>"; };
812+
6A3B84F429F075A500DE5CE0 /* Thread+Name.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Thread+Name.swift"; sourceTree = "<group>"; };
810813
6A3C1EB028D7B3C700D51E44 /* CleverTapSDK.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CleverTapSDK.framework; sourceTree = BUILT_PRODUCTS_DIR; };
811814
6A3C1EB128D7B3C700D51E44 /* SDWebImage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SDWebImage.framework; sourceTree = BUILT_PRODUCTS_DIR; };
812815
6A714BA326F8B317004A34A9 /* Leanplum.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Leanplum.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -1421,6 +1424,7 @@
14211424
6ADC90C62807461F00CE42C7 /* Dictionary+ValueKeyPath.swift */,
14221425
6A07FD9E280F27C700995BE3 /* NSRegularExpression+Matches.swift */,
14231426
6A37A89B28EF748800F4339F /* Dictionary+MapKeys.swift */,
1427+
6A3B84F429F075A500DE5CE0 /* Thread+Name.swift */,
14241428
);
14251429
path = Extensions;
14261430
sourceTree = "<group>";
@@ -1918,6 +1922,7 @@
19181922
075AAE0326847EC4007CA1BD /* LPFileTransferManager.m in Sources */,
19191923
6A96032A27C69F3C00F34BA0 /* WeakTimer.swift in Sources */,
19201924
075AAE3A26847EC4007CA1BD /* LPWebInterstitialMessageTemplate.m in Sources */,
1925+
6A3B84F529F075A500DE5CE0 /* Thread+Name.swift in Sources */,
19211926
075AAE8B26847EC4007CA1BD /* LPUtils.m in Sources */,
19221927
075AADE926847EC4007CA1BD /* LPMessageTemplates.m in Sources */,
19231928
39C081A32781D40200C1DBD6 /* ActionManager+Definition.swift in Sources */,
@@ -2047,6 +2052,7 @@
20472052
6A714B9426F8B317004A34A9 /* LPFileTransferManager.m in Sources */,
20482053
6A96032B27C69F3C00F34BA0 /* WeakTimer.swift in Sources */,
20492054
6A714B9526F8B317004A34A9 /* LPWebInterstitialMessageTemplate.m in Sources */,
2055+
6A3B84F629F075A500DE5CE0 /* Thread+Name.swift in Sources */,
20502056
6A714B9626F8B317004A34A9 /* LPUtils.m in Sources */,
20512057
6A714B9726F8B317004A34A9 /* LPMessageTemplates.m in Sources */,
20522058
39C081AB278D995300C1DBD6 /* ActionManager+Scheduler.swift in Sources */,

LeanplumSDK/LeanplumSDK/Classes/Features/Actions/LPActionContext.m

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// Leanplum-iOS-SDK-source
44
//
55
// Created by Mayank Sanganeria on 4/24/18.
6-
//
6+
// Copyright (c) 2023 Leanplum, Inc. All rights reserved.
77

88
#import "LeanplumInternal.h"
99
#import "LPVarCache.h"
@@ -482,9 +482,9 @@ - (void)runActionNamed:(NSString *)name
482482
chainedActionContext->_isRooted = self->_isRooted;
483483
chainedActionContext->_isChainedMessage = YES;
484484
chainedActionContext->_parentContext = weakSelf;
485-
dispatch_async(dispatch_get_main_queue(), ^{
485+
[LPUtils dispatchOnMainQueue:^{
486486
[[LPActionManager shared] triggerWithContexts:@[chainedActionContext] priority:PriorityHigh trigger:nil];
487-
});
487+
}];
488488
};
489489

490490
if (messageId && [actionType isEqualToString:LP_VALUE_CHAIN_MESSAGE_ACTION_NAME]) {
@@ -501,7 +501,15 @@ - (void)runActionNamed:(NSString *)name
501501
if (message) {
502502
executeChainedMessage();
503503
}
504-
LPActionManager.shared.isPaused = previousPauseState;
504+
if ([[LPActionManager shared] useAsyncHandlers]) {
505+
dispatch_async([[LPActionManager shared] actionQueue], ^{
506+
dispatch_async(dispatch_get_main_queue(), ^{
507+
LPActionManager.shared.isPaused = previousPauseState;
508+
});
509+
});
510+
} else {
511+
LPActionManager.shared.isPaused = previousPauseState;
512+
}
505513
}];
506514
}
507515
} else {
@@ -514,9 +522,9 @@ - (void)runActionNamed:(NSString *)name
514522
childContext->_isRooted = _isRooted;
515523
childContext->_parentContext = weakSelf;
516524
childContext->_key = name;
517-
dispatch_async(dispatch_get_main_queue(), ^{
525+
[LPUtils dispatchOnMainQueue:^{
518526
[[LPActionManager shared] triggerWithContexts:@[childContext] priority:PriorityHigh trigger:nil];
519-
});
527+
}];
520528
}
521529

522530
LP_END_TRY

LeanplumSDK/LeanplumSDK/Classes/Utilities/LPUtils.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// Leanplum
44
//
55
// Created by Ben Marten on 6/6/16.
6-
// Copyright (c) 2016 Leanplum, Inc. All rights reserved.
6+
// Copyright (c) 2023 Leanplum, Inc. All rights reserved.
77
//
88
// Licensed to the Apache Software Foundation (ASF) under one
99
// or more contributor license agreements. See the NOTICE file
@@ -72,4 +72,6 @@
7272
*/
7373
+ (BOOL)isBoolNumber:(NSNumber * _Nonnull)value;
7474

75+
+ (void)dispatchOnMainQueue:(void (^_Nonnull)(void))block;
76+
7577
@end

LeanplumSDK/LeanplumSDK/Classes/Utilities/LPUtils.m

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// Leanplum
44
//
55
// Created by Ben Marten on 6/6/16.
6-
// Copyright (c) 2016 Leanplum, Inc. All rights reserved.
6+
// Copyright (c) 2023 Leanplum, Inc. All rights reserved.
77
//
88
// Licensed to the Apache Software Foundation (ASF) under one
99
// or more contributor license agreements. See the NOTICE file
@@ -126,4 +126,15 @@ + (BOOL)isBoolNumber:(NSNumber *)value
126126
return numID == boolID;
127127
}
128128

129+
+ (void)dispatchOnMainQueue:(void (^_Nonnull)(void))block
130+
{
131+
if ([NSThread isMainThread]) {
132+
block();
133+
} else {
134+
dispatch_async(dispatch_get_main_queue(), ^{
135+
block();
136+
});
137+
}
138+
}
139+
129140
@end

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

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ extension ActionManager {
2525
let definition = definitions.first { $0.name == action.context.name }
2626
let _ = definition?.dismissAction?(action.context)
2727

28-
Log.debug("[ActionManager]: asking for dismissal: \(action.context).")
28+
Log.debug("\(ActionManager.logTag): asking for dismissal: \(action.context).")
2929
}
3030
return
3131
}
@@ -36,11 +36,11 @@ extension ActionManager {
3636
return
3737
}
3838

39-
Log.debug("[ActionManager]: running action with name: \(action.context).")
39+
Log.debug("\(ActionManager.logTag): running action with name: \(action.context).")
4040

4141
if action.type == .single,
4242
Leanplum.shouldSuppressMessage(action.context) {
43-
Log.info("[ActionManager]: local IAM caps reached, suppressing \(action.context).")
43+
Log.info("\(ActionManager.logTag): local IAM caps reached, suppressing \(action.context).")
4444
state.currentAction = nil
4545
performAvailableActions()
4646
return
@@ -59,7 +59,7 @@ extension ActionManager {
5959
// if message is delayed, add it to the scheduler to be delayed
6060
// by the amount of seconds, and exit
6161
if case .delay(let amount) = messageDisplayDecision?.decision {
62-
Log.debug("[ActionManager]: delaying action: \(action.context) for \(amount)s.")
62+
Log.debug("\(ActionManager.logTag): delaying action: \(action.context) for \(amount)s.")
6363

6464
if amount > 0 {
6565
// Schedule for delayed time
@@ -81,41 +81,67 @@ extension ActionManager {
8181
// get the action definition
8282
let definition = self?.definitions.first { $0.name == action.context.name }
8383

84+
let actionDidExecute: (ActionContext) -> () = { [weak self] context in
85+
Log.debug("\(ActionManager.logTag): actionDidExecute: \(context).")
86+
self?.onMessageAction?(context.name, context)
87+
}
88+
8489
// 2) set the execute block which will be called by client
8590
action.context.actionDidExecute = { [weak self] context in
86-
Log.debug("[ActionManager]: actionDidExecute: \(context).")
87-
self?.onMessageAction?(context.name, context)
91+
if self?.useAsyncHandlers == true {
92+
self?.actionQueue.async {
93+
actionDidExecute(context)
94+
}
95+
} else {
96+
actionDidExecute(context)
97+
}
8898
}
8999

90-
// 3) set the dismiss block which will be called by client
91-
action.context.actionDidDismiss = { [weak self] in
92-
Log.debug("[ActionManager]: actionDidDismiss: \(action.context).")
100+
let actionDidDismiss = { [weak self] in
101+
Log.debug("\(ActionManager.logTag): actionDidDismiss: \(action.context).")
93102
self?.onMessageDismissed?(action.context)
94103
self?.state.currentAction = nil
95104
self?.performAvailableActions()
96105
}
97106

107+
// 3) set the dismiss block which will be called by client
108+
action.context.actionDidDismiss = { [weak self] in
109+
if self?.useAsyncHandlers == true {
110+
self?.actionQueue.async {
111+
actionDidDismiss()
112+
}
113+
} else {
114+
actionDidDismiss()
115+
}
116+
}
117+
98118
// 1) ask to present, return if its not
99119
guard let handled = definition?.presentAction?(action.context), handled else {
100-
Log.debug("[ActionManager]: action NOT presented: \(action.context).")
120+
Log.debug("\(ActionManager.logTag): action NOT presented: \(action.context).")
101121
self?.state.currentAction = nil
102122
self?.performAvailableActions()
103123
return
104124
}
105-
Log.info("[ActionManager]: action presented: \(action.context).")
125+
Log.info("\(ActionManager.logTag): action presented: \(action.context).")
106126

107127
// iff handled track that message has been displayed
108128
// propagate event that message is displayed
109-
self?.onMessageDisplayed?(action.context)
129+
if self?.useAsyncHandlers == true {
130+
self?.actionQueue.async { [weak self] in
131+
self?.onMessageDisplayed?(action.context)
132+
}
133+
} else {
134+
self?.onMessageDisplayed?(action.context)
135+
}
110136

111137
// record the impression
112138
self?.recordImpression(action: action)
113139
}
114140
}
115141

116142
func shouldDisplayMessage(context: ActionContext, callback: @escaping (MessageDisplayChoice?) -> ()) {
117-
if useAsyncDecisionHandlers {
118-
DispatchQueue.global(qos: .background).async { [weak self] in
143+
if useAsyncHandlers {
144+
actionQueue.async { [weak self] in
119145
let messageDisplayDecision = self?.shouldDisplayMessage?(context)
120146
DispatchQueue.main.async {
121147
callback(messageDisplayDecision)
@@ -126,4 +152,8 @@ extension ActionManager {
126152
callback(messageDisplayDecision)
127153
}
128154
}
155+
156+
static var logTag: String {
157+
"[ActionManager][\(Thread.current.threadName)]"
158+
}
129159
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ extension ActionManager {
5252
}
5353

5454
func prioritizeMessages(contexts: [ActionContext], defaultContexts: [ActionContext], trigger: ActionsTrigger? = nil, callback: @escaping ([ActionContext]) -> ()) {
55-
if useAsyncDecisionHandlers {
56-
DispatchQueue.global(qos: .background).async { [weak self] in
55+
if useAsyncHandlers {
56+
actionQueue.async { [weak self] in
5757
let filteredActions = self?.prioritizeMessages?(contexts, trigger) ?? defaultContexts
5858
DispatchQueue.main.async {
5959
callback(filteredActions)

LeanplumSDK/LeanplumSDK/ClassesSwift/Actions/ActionManager.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import Foundation
1515
/// Set a new configuration to override a configuration option
1616
public var configuration: Configuration = .default
1717

18-
public var useAsyncDecisionHandlers = false
18+
public var useAsyncHandlers = false
19+
public let actionQueue = DispatchQueue(label: "leanplum.background_action_queue", qos: .background, target: DispatchQueue.global())
1920

2021
lazy var queue: Queue = Queue()
2122
lazy var delayedQueue: Queue = Queue()
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//
2+
// Thread+Name.swift
3+
// LeanplumSDK
4+
//
5+
// Created by Nikola Zagorchev on 19.04.23.
6+
// Copyright © 2023 Leanplum. All rights reserved.
7+
8+
import Foundation
9+
10+
extension Thread {
11+
var threadName: String {
12+
if isMainThread {
13+
return "main"
14+
} else if let threadName = Thread.current.name, !threadName.isEmpty {
15+
return threadName
16+
} else {
17+
return "background"
18+
}
19+
}
20+
}

0 commit comments

Comments
 (0)