Skip to content

Commit 3c352a8

Browse files
authored
Allow early initialization of CleverTap (#541)
* Enable CT wrapper early initialization * Fixes for early initialization * Handle push on launch
1 parent c32c5e4 commit 3c352a8

File tree

9 files changed

+98
-54
lines changed

9 files changed

+98
-54
lines changed

LeanplumSDK/LeanplumSDK/Classes/Internal/Leanplum.m

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ - (void)leanplum_cancelRequestWithError:(NSError *)error
9494

9595
@implementation Leanplum
9696

97-
+(void)load
97+
+ (void)load
9898
{
9999
static dispatch_once_t onceToken;
100100
dispatch_once(&onceToken, ^{
@@ -103,6 +103,11 @@ +(void)load
103103
}
104104
});
105105

106+
[self setUsingPlist];
107+
}
108+
109+
+ (void)setUsingPlist
110+
{
106111
NSDictionary *appKeysDictionary = [self getDefaultAppKeysPlist];
107112
if (appKeysDictionary == nil) {
108113
return;
@@ -916,11 +921,7 @@ + (void)startWithUserId:(NSString *)userId
916921
withContextualValues:nil];
917922
}];
918923

919-
[[MigrationManager shared] fetchMigrationState:^{
920-
if ([[MigrationManager shared] useCleverTap]) {
921-
[[MigrationManager shared] launch];
922-
}
923-
924+
[[MigrationManager shared] fetchMigrationState:^{
924925
if ([[MigrationManager shared] useLeanplum]) {
925926
// hasStarted and startSuccessful will be set from the request callbacks
926927
// triggerStartResponse will be called from the request callbacks

LeanplumSDK/LeanplumSDK/ClassesSwift/Migration/LPCTNotificationsManager.swift

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,19 @@ import Foundation
2323
}
2424
}
2525

26-
override func notificationOpened(userInfo: [AnyHashable : Any], action: String = LP_VALUE_DEFAULT_PUSH_ACTION) {
26+
override func notificationOpened(userInfo: [AnyHashable : Any], action: String = LP_VALUE_DEFAULT_PUSH_ACTION, fromLaunch: Bool = false) {
2727
if Utilities.messageIdFromUserInfo(userInfo) != nil {
2828
// Handle Leanplum notifications
2929
super.notificationOpened(userInfo: userInfo, action: action)
3030
return
3131
}
32-
handlerCleverTapNotification(userInfo: userInfo, event: .opened)
32+
// If the app is launched from notification and CT instance has already been created,
33+
// CT will handle the notification from their UIApplication didFinishLaunchingNotification observer
34+
if fromLaunch && MigrationManager.shared.hasLaunched {
35+
return
36+
}
37+
38+
handleCleverTapNotification(userInfo: userInfo, event: .opened)
3339
}
3440

3541
override func notificationReceived(userInfo: [AnyHashable : Any], isForeground: Bool) {
@@ -38,31 +44,38 @@ import Foundation
3844
super.notificationReceived(userInfo: userInfo, isForeground: isForeground)
3945
return
4046
}
41-
handlerCleverTapNotification(userInfo: userInfo, event: .received)
47+
handleCleverTapNotification(userInfo: userInfo, event: .received)
4248
}
4349

4450
public override func didRegisterForRemoteNotificationsWithDeviceToken(_ deviceToken: Data) {
4551
super.didRegisterForRemoteNotificationsWithDeviceToken(deviceToken)
4652

4753
Log.debug("[Wrapper] Will call CleverTap.setPushToken for didRegisterForRemoteNotifications, when Leanplum has issued start.")
48-
// Leanplum.onStartIssued guarantees that Wrapper is initialized and CT instance is available, if migration has started.
49-
Leanplum.onStartIssued {
50-
if MigrationManager.shared.useCleverTap {
51-
MigrationManager.shared.setPushToken(deviceToken)
52-
}
54+
handleWithCleverTapInstance {
55+
MigrationManager.shared.setPushToken(deviceToken)
5356
}
5457
}
5558

56-
func handlerCleverTapNotification(userInfo: [AnyHashable : Any], event: NotificationEvent) {
59+
func handleCleverTapNotification(userInfo: [AnyHashable : Any], event: NotificationEvent) {
5760
Log.debug("[Wrapper] Will call CleverTap.handlePushNotification for Push \(event), when Leanplum has issued start.")
58-
// Leanplum.onStartIssued guarantees that Wrapper is initialized and CT instance is available, if migration has started.
59-
Leanplum.onStartIssued {
60-
if MigrationManager.shared.useCleverTap {
61-
Log.debug("""
62-
[Wrapper] Calling CleverTap.handlePushNotification:openDeepLinksInForeground: \
63-
\(Constants.OpenDeepLinksInForeground) for Push \(event)
64-
""")
65-
CleverTap.handlePushNotification(userInfo, openDeepLinksInForeground: Constants.OpenDeepLinksInForeground)
61+
handleWithCleverTapInstance {
62+
Log.debug("""
63+
[Wrapper] Calling CleverTap.handlePushNotification:openDeepLinksInForeground: \
64+
\(Constants.OpenDeepLinksInForeground) for Push \(event)
65+
""")
66+
CleverTap.handlePushNotification(userInfo, openDeepLinksInForeground: Constants.OpenDeepLinksInForeground)
67+
}
68+
}
69+
70+
func handleWithCleverTapInstance(action: @escaping () -> ()) {
71+
if MigrationManager.shared.hasLaunched {
72+
action()
73+
} else {
74+
// Leanplum.onStartIssued guarantees that Wrapper is initialized and CT instance is available, if migration has started.
75+
Leanplum.onStartIssued {
76+
if MigrationManager.shared.useCleverTap {
77+
action()
78+
}
6679
}
6780
}
6881
}

LeanplumSDK/LeanplumSDK/ClassesSwift/Migration/MigrationManager+API.swift

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,25 @@
66
// Copyright © 2022 Leanplum. All rights reserved.
77

88
@objc public extension MigrationManager {
9-
10-
func launch() {
11-
guard let wrapper = wrapper else {
12-
Log.debug("[Wrapper] Calling launch before wrapper is initialized.")
13-
return
14-
}
15-
wrapper.launch()
16-
}
17-
189
var state: MigrationState {
1910
return migrationState
2011
}
12+
13+
var hasLaunched: Bool {
14+
guard let wrapper = wrapper else { return false }
15+
16+
return wrapper.hasLaunched
17+
}
18+
19+
// Expose to ObjC
20+
var useLeanplum: Bool {
21+
migrationState.useLeanplum
22+
}
23+
24+
// Expose to ObjC
25+
var useCleverTap: Bool {
26+
migrationState.useCleverTap
27+
}
2128

2229
func track(_ eventName: String?, value: Double, info: String?, params: [String: Any]) {
2330
wrapper?.track(eventName, value: value, info: info, params: params)

LeanplumSDK/LeanplumSDK/ClassesSwift/Migration/MigrationManager.swift

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,23 +44,19 @@ import Foundation
4444

4545
private let lock = NSLock()
4646

47-
// Expose to ObjC
48-
@objc public var useLeanplum: Bool {
49-
migrationState.useLeanplum
50-
}
51-
52-
// Expose to ObjC
53-
@objc public var useCleverTap: Bool {
54-
migrationState.useCleverTap
55-
}
56-
57-
func initWrapper() {
47+
@objc public func launchWrapper() {
5848
if migrationState.useCleverTap, wrapper == nil {
5949
guard let id = accountId, let token = accountToken, let accountRegion = regionCode else {
6050
Log.error("[Wrapper] Missing CleverTap Credentials. Cannot initialize CleverTap.")
6151
return
6252
}
63-
guard let user = Leanplum.userId(), let device = Leanplum.deviceId() else {
53+
54+
if !LPInternalState.shared().calledStart {
55+
Log.info("[Wrapper] Initializing before calling start. Loading user.")
56+
loadUser()
57+
}
58+
59+
guard let user = Leanplum.user.userId, let device = Leanplum.user.deviceId else {
6460
Log.error("[Wrapper] Missing Leanplum userId and deviceId. Cannot initialize CleverTap.")
6561
return
6662
}
@@ -69,12 +65,31 @@ import Foundation
6965
accountRegion: accountRegion,
7066
userId: user, deviceId: device,
7167
callbacks: instanceCallbacks)
72-
73-
if Leanplum.hasStarted() {
74-
Log.debug("[Wrapper] Leanplum has already started, launching CleverTap as well.")
75-
wrapper?.launch()
68+
wrapper?.launch()
69+
}
70+
}
71+
72+
func loadUser() {
73+
guard let encryptedDiffs = UserDefaults.standard.data(forKey: LEANPLUM_DEFAULTS_VARIABLES_KEY),
74+
let diffsData = LPAES.decryptedData(from: encryptedDiffs) else { return }
75+
76+
var unarchiver: NSKeyedUnarchiver?
77+
78+
if #available(iOS 11.0, *) {
79+
do {
80+
unarchiver = try NSKeyedUnarchiver(forReadingFrom: diffsData)
81+
} catch {
82+
Log.error("[Wrapper] Error unarchiving userId and deviceId.")
7683
}
84+
} else {
85+
unarchiver = NSKeyedUnarchiver(forReadingWith: diffsData)
7786
}
87+
88+
unarchiver?.requiresSecureCoding = false
89+
90+
Leanplum.user.deviceId = unarchiver?.decodeObject(forKey: LP_PARAM_DEVICE_ID) as? String
91+
92+
Leanplum.user.userId = unarchiver?.decodeObject(forKey: LP_PARAM_USER_ID) as? String
7893
}
7994

8095
func handleMigrationStateChanged(oldValue: MigrationState) {
@@ -84,7 +99,7 @@ import Foundation
8499
// Flush all saved requests to Leanplum
85100
LPRequestSender.sharedInstance().sendRequests()
86101
// Create wrapper
87-
initWrapper()
102+
launchWrapper()
88103
}
89104

90105
if (oldValue.useLeanplum && !migrationState.useLeanplum) {
@@ -108,7 +123,7 @@ import Foundation
108123

109124
@objc public func fetchMigrationState(_ completion: @escaping ()->()) {
110125
if migrationState != .undefined {
111-
initWrapper()
126+
launchWrapper()
112127
completion()
113128
return
114129
}

LeanplumSDK/LeanplumSDK/ClassesSwift/Migration/Wrapper/CTWrapper.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ class CTWrapper: Wrapper {
7979
triggerInstanceCallbacks()
8080
}
8181

82+
var hasLaunched: Bool {
83+
return cleverTapInstance != nil
84+
}
85+
8286
// MARK: Callback
8387
func addInstanceCallback(_ callback: CleverTapInstanceCallback) {
8488
instanceCallbacks.append(callback)

LeanplumSDK/LeanplumSDK/ClassesSwift/Migration/Wrapper/Wrapper.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ protocol Wrapper {
1010
/// equivalent to Leanplum start
1111
func launch()
1212

13+
var hasLaunched: Bool { get }
14+
1315
/// Adds instance callback, executed when wrapper has initialized
1416
func addInstanceCallback(_ callback: CleverTapInstanceCallback)
1517

LeanplumSDK/LeanplumSDK/ClassesSwift/Notifications/NotificationsManager.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ import UIKit
8585

8686
// MARK: - Notification Actions
8787
// MARK: Notification Open
88-
@objc(notificationOpened:action:)
89-
func notificationOpened(userInfo: [AnyHashable: Any], action: String = LP_VALUE_DEFAULT_PUSH_ACTION) {
88+
@objc(notificationOpened:action:fromLaunch:)
89+
func notificationOpened(userInfo: [AnyHashable: Any], action: String = LP_VALUE_DEFAULT_PUSH_ACTION, fromLaunch: Bool = false) {
9090
guard let messageId = Utilities.messageIdFromUserInfo(userInfo) else {
9191
Log.debug("Push notification not handled, no message id found.")
9292
return

LeanplumSDK/LeanplumSDK/ClassesSwift/Notifications/Proxy/NotificationsProxy.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public class NotificationsProxy: NSObject {
7575
Leanplum.notificationsManager().notificationReceived(userInfo: remoteNotification, isForeground: false)
7676
} else {
7777
notificationOpenedFromStart = true
78-
Leanplum.notificationsManager().notificationOpened(userInfo: remoteNotification)
78+
Leanplum.notificationsManager().notificationOpened(userInfo: remoteNotification, action: LP_VALUE_DEFAULT_PUSH_ACTION, fromLaunch: true)
7979
}
8080
} else if
8181
let localNotification =

LeanplumSDKApp/LeanplumSDKTests/Classes/Notifications/NotificationsManagerMock.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ class NotificationsManagerMock: NotificationsManager {
2222
public var methodInvocations = 0
2323
public var isPushEnabled: Bool?
2424

25-
override func notificationOpened(userInfo: [AnyHashable : Any], action: String = LP_VALUE_DEFAULT_PUSH_ACTION) {
25+
override func notificationOpened(userInfo: [AnyHashable : Any],
26+
action: String = LP_VALUE_DEFAULT_PUSH_ACTION,
27+
fromLaunch: Bool = false) {
2628
userInfoProcessed = userInfo
2729
actionName = action
2830
methodInvocations += 1

0 commit comments

Comments
 (0)