Skip to content

Commit e05f600

Browse files
committed
add OSModelStore.refresh() to re-hydrate from UserDefaults
Lets callers re-read from disk after protected-data becomes available (iOS prewarm fix). No-op when models already populated; doesn't fire listeners. Extract UD read + change-notifier subscribe into helpers shared with init().
1 parent 1b44eed commit e05f600

1 file changed

Lines changed: 31 additions & 9 deletions

File tree

iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSModelStore.swift

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,20 @@ open class OSModelStore<TModel: OSModel>: NSObject {
3636
public init(changeSubscription: OSEventProducer<OSModelStoreChangedHandler>, storeKey: String) {
3737
self.storeKey = storeKey
3838
self.changeSubscription = changeSubscription
39-
40-
// read models from cache, if any
41-
if let models = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: self.storeKey, defaultValue: [:]) as? [String: TModel] {
42-
self.models = models
43-
} else {
44-
// log error
45-
self.models = [:]
46-
}
39+
self.models = OSModelStore.loadModelsFromUserDefaults(storeKey: storeKey)
4740
super.init()
41+
subscribeToOwnedModels()
42+
}
43+
44+
/// Reads the archived `[String: TModel]` dict for `storeKey` from shared UserDefaults.
45+
/// Returns an empty dict if the entry is missing or the unarchive fails.
46+
private static func loadModelsFromUserDefaults(storeKey: String) -> [String: TModel] {
47+
return (OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: storeKey, defaultValue: [:]) as? [String: TModel]) ?? [:]
48+
}
4849

49-
// listen for changes to the models
50+
/// Subscribes this store as a change observer on every model currently in `models`.
51+
/// Callers must hold `lock` if invoking after init.
52+
private func subscribeToOwnedModels() {
5053
for model in self.models.values {
5154
model.changeNotifier.subscribe(self)
5255
}
@@ -93,6 +96,25 @@ open class OSModelStore<TModel: OSModel>: NSObject {
9396
}
9497
}
9598

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

0 commit comments

Comments
 (0)