Skip to content

Commit 309da62

Browse files
committed
asdf
1 parent d589e34 commit 309da62

3 files changed

Lines changed: 48 additions & 27 deletions

File tree

iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSModelStore.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,29 @@ open class OSModelStore<TModel: OSModel>: NSObject {
9393
}
9494
}
9595

96+
/// Re-read this store's backing UserDefaults entry and hydrate `models` from disk.
97+
/// No-op when `models` is already non-empty — we never clobber in-memory state.
98+
///
99+
/// Motivation: model stores load their `models` dict once in `init()` from shared UserDefaults.
100+
/// If `init()` runs while protected data is unavailable (iOS app prewarm, NSE before first
101+
/// unlock), that read returns nil and the dict stays empty for the lifetime of the singleton —
102+
/// it is never re-read. After protected data becomes available, callers can call `refresh()`
103+
/// so the store reflects what's actually on disk. Does not fire listener events.
104+
public func refresh() {
105+
lock.withLock {
106+
guard models.isEmpty else { return }
107+
guard let stored = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: self.storeKey, defaultValue: [:]) as? [String: TModel],
108+
!stored.isEmpty else {
109+
return
110+
}
111+
OneSignalLog.onesignalLog(.LL_DEBUG, message: "OSModelStore[\(self.storeKey)] refresh hydrated \(stored.count) model(s) from UserDefaults")
112+
self.models = stored
113+
for model in stored.values {
114+
model.changeNotifier.subscribe(self)
115+
}
116+
}
117+
}
118+
96119
public func add(id: String, model: TModel, hydrating: Bool) {
97120
// TODO: Check if we are adding the same model? Do we replace?
98121
// For example, calling addEmail multiple times with the same email

iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,16 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager {
215215

216216
OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OneSignalUserManager calling start")
217217

218+
// The model stores load their in-memory `models` dict once in their initializer.
219+
// If the singleton was first touched while protected data was unavailable (iOS app
220+
// prewarm), that read returned nil and the dicts are empty. The gate above ensures
221+
// `start()` only proceeds when protected data is available, but the stores need to
222+
// be refreshed here so Path 1's cache check can see what's on disk.
223+
identityModelStore.refresh()
224+
propertiesModelStore.refresh()
225+
subscriptionModelStore.refresh()
226+
pushSubscriptionModelStore.refresh()
227+
218228
OSNotificationsManager.delegate = self
219229

220230
var hasCachedUser = false
@@ -266,25 +276,6 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager {
266276
OneSignalUserDefaults.initShared().saveString(forKey: OSUD_PUSH_SUBSCRIPTION_ID, withValue: legacyPlayerId)
267277
OneSignalUserDefaults.initStandard().removeValue(forKey: OSUD_LEGACY_PLAYER_ID)
268278
OneSignalUserDefaults.initShared().removeValue(forKey: OSUD_LEGACY_PLAYER_ID)
269-
} else if !hasCachedUser, _user == nil,
270-
let cachedSubId = OSResilientStorage.string(forKey: OSResilientStorage.keySubscriptionId), !cachedSubId.isEmpty {
271-
// Path 2.5. Model stores looked empty, but the resilient cache shows we've
272-
// initialized before — the prewarm-before-first-unlock case where
273-
// OSModelStore's in-memory dict was loaded empty during locked storage and
274-
// never re-read. Recover the existing identity locally; missing properties /
275-
// aliases will rehydrate on the next fetchUser. Do NOT call setNewInternalUser
276-
// (which would write stub models through modelStore.add) — that would
277-
// overwrite the real cached models on disk.
278-
let cachedOnesignalId = OSResilientStorage.string(forKey: OSResilientStorage.keyOneSignalId)
279-
OneSignalLog.onesignalLog(.LL_DEBUG, message: "OneSignalUserManager: recovering user from resilient cache subscription_id=\(cachedSubId) onesignal_id=\(cachedOnesignalId ?? "(nil)")")
280-
let identityModel = OSIdentityModel(aliases: nil, changeNotifier: OSEventProducer())
281-
if let onesignalId = cachedOnesignalId, !onesignalId.isEmpty {
282-
identityModel.hydrate([OS_ONESIGNAL_ID: onesignalId])
283-
}
284-
addIdentityModelToRepo(identityModel)
285-
let propertiesModel = OSPropertiesModel(changeNotifier: OSEventProducer())
286-
let pushSubscriptionModel = createDefaultPushSubscription(subscriptionId: cachedSubId)
287-
_user = OSUserInternalImpl(identityModel: identityModel, propertiesModel: propertiesModel, pushSubscriptionModel: pushSubscriptionModel)
288279
} else {
289280
// Path 3. Creates an anonymous user if there isn't one in the cache nor a legacy player
290281
if _user == nil {
@@ -899,28 +890,21 @@ extension OneSignalUserManagerImpl {
899890
guard !OneSignalConfig.shouldAwaitAppIdAndLogMissingPrivacyConsent(forMethod: "pushSubscription.id") else {
900891
return nil
901892
}
902-
// Fall back to the live `_user.pushSubscriptionModel` so the prewarm-recovery
903-
// path (where the model isn't added to the store) still returns the right value.
904893
return pushSubscriptionModelStore.getModel(key: OS_PUSH_SUBSCRIPTION_MODEL_KEY)?.subscriptionId
905-
?? OneSignalUserManagerImpl.sharedInstance._user?.pushSubscriptionModel.subscriptionId
906894
}
907895

908896
public var token: String? {
909897
guard !OneSignalConfig.shouldAwaitAppIdAndLogMissingPrivacyConsent(forMethod: "pushSubscription.token") else {
910898
return nil
911899
}
912900
return pushSubscriptionModelStore.getModel(key: OS_PUSH_SUBSCRIPTION_MODEL_KEY)?.address
913-
?? OneSignalUserManagerImpl.sharedInstance._user?.pushSubscriptionModel.address
914901
}
915902

916903
public var optedIn: Bool {
917904
guard !OneSignalConfig.shouldAwaitAppIdAndLogMissingPrivacyConsent(forMethod: "pushSubscription.optedIn") else {
918905
return false
919906
}
920-
if let storeModel = pushSubscriptionModelStore.getModel(key: OS_PUSH_SUBSCRIPTION_MODEL_KEY) {
921-
return storeModel.optedIn
922-
}
923-
return OneSignalUserManagerImpl.sharedInstance._user?.pushSubscriptionModel.optedIn ?? false
907+
return pushSubscriptionModelStore.getModel(key: OS_PUSH_SUBSCRIPTION_MODEL_KEY)?.optedIn ?? false
924908
}
925909

926910
/**

iOS_SDK/OneSignalSDK/Source/OneSignal.m

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,20 @@ + (void)init {
465465
return UIApplication.sharedApplication.isProtectedDataAvailable;
466466
};
467467

468+
// When the device unlocks after a locked-storage launch (iOS app prewarm), drive `start()`
469+
// again. `start()` was gated by the predicate while protected data was unavailable; the
470+
// re-call now sees the gate clear, refreshes the model stores from shared UserDefaults,
471+
// and takes the normal Path 1 cache load.
472+
static dispatch_once_t protectedDataObserverOnce;
473+
dispatch_once(&protectedDataObserverOnce, ^{
474+
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationProtectedDataDidBecomeAvailable
475+
object:nil
476+
queue:nil
477+
usingBlock:^(NSNotification * _Nonnull note) {
478+
[OneSignalUserManagerImpl.sharedInstance start];
479+
}];
480+
});
481+
468482
// TODO: We moved this check to the top of this method, we should test this.
469483
if (initDone) {
470484
return;

0 commit comments

Comments
 (0)