Skip to content

Commit ae07b4e

Browse files
committed
fddsgdgsdg
1 parent 309da62 commit ae07b4e

2 files changed

Lines changed: 46 additions & 18 deletions

File tree

iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OneSignalConfig.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,16 @@ import OneSignalCore
3232
@objc(OneSignalConfig)
3333
public final class OneSignalConfig: NSObject {
3434

35-
/// Optional readability check for device-protected storage. The main app sets this to
36-
/// `{ UIApplication.shared.isProtectedDataAvailable }` at initialize. Left nil in
37-
/// app-extension contexts (NSE) since `UIApplication` is unavailable there.
35+
/// Optional readability check for device-protected storage.
36+
///
37+
/// Set-once invariant: the main app assigns this exactly once during `OneSignal.init`,
38+
/// inside a `dispatch_once`. The assignment is the publishing side and `dispatch_once`
39+
/// is a memory barrier, so concurrent readers via `shouldAwait…` observe a stable closure.
40+
/// The closure itself must be cheap and thread-safe — the main app implements it as an
41+
/// atomic-BOOL load fed by `UIApplicationProtectedData{Did,Will}BecomeAvailable` so we
42+
/// never touch the main-thread-only `UIApplication` from arbitrary callers.
43+
///
44+
/// Left nil in app-extension contexts (NSE) since `UIApplication` is unavailable there.
3845
/// When nil the predicate treats protected data as available — the right default for NSE,
3946
/// which reads identifiers through `OSResilientStorage` (file-backed, bypasses cfprefsd).
4047
@objc public static var isProtectedDataAvailableProvider: (() -> Bool)?

iOS_SDK/OneSignalSDK/Source/OneSignal.m

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
* THE SOFTWARE.
2626
*/
2727

28+
#import <stdatomic.h>
2829
#import "OneSignalFramework.h"
2930
#import <OneSignalOSCore/OneSignalOSCore-Swift.h>
3031
#import "OneSignalInternal.h"
@@ -461,22 +462,41 @@ + (void)init {
461462
// operations during iOS prewarm (before first unlock) when shared UserDefaults reads
462463
// are unreliable. UIApplication isn't available to OneSignalOSCore (which OneSignalConfig
463464
// lives in), so the main app injects the check here at initialize time.
464-
OneSignalConfig.isProtectedDataAvailableProvider = ^BOOL {
465-
return UIApplication.sharedApplication.isProtectedDataAvailable;
466-
};
467-
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) {
465+
//
466+
// The flag is cached in an atomic BOOL and updated from
467+
// UIApplicationProtectedData{DidBecomeAvailable,WillBecomeUnavailable} so we never touch
468+
// `UIApplication` synchronously from the arbitrary threads that hit the predicate.
469+
static _Atomic(BOOL) gProtectedDataAvailable = NO;
470+
static dispatch_once_t protectedDataOnce;
471+
dispatch_once(&protectedDataOnce, ^{
472+
// Seed the cache on the main thread once. UIApplication APIs are main-thread-only.
473+
// On a normal launch this resolves to YES near-immediately; on a prewarm launch
474+
// before first unlock it stays NO and the notification will flip it later.
475+
dispatch_async(dispatch_get_main_queue(), ^{
476+
atomic_store(&gProtectedDataAvailable, UIApplication.sharedApplication.isProtectedDataAvailable);
477+
});
478+
479+
NSNotificationCenter *center = NSNotificationCenter.defaultCenter;
480+
[center addObserverForName:UIApplicationProtectedDataDidBecomeAvailable
481+
object:nil
482+
queue:nil
483+
usingBlock:^(NSNotification * _Nonnull note) {
484+
atomic_store(&gProtectedDataAvailable, YES);
485+
// Drive `start()` again — it was gated by the predicate while protected data was
486+
// unavailable. The re-call now sees the gate clear, refreshes the model stores
487+
// from shared UserDefaults, and takes the normal Path 1 cache load.
478488
[OneSignalUserManagerImpl.sharedInstance start];
479489
}];
490+
[center addObserverForName:UIApplicationProtectedDataWillBecomeUnavailable
491+
object:nil
492+
queue:nil
493+
usingBlock:^(NSNotification * _Nonnull note) {
494+
atomic_store(&gProtectedDataAvailable, NO);
495+
}];
496+
497+
OneSignalConfig.isProtectedDataAvailableProvider = ^BOOL {
498+
return atomic_load(&gProtectedDataAvailable);
499+
};
480500
});
481501

482502
// TODO: We moved this check to the top of this method, we should test this.
@@ -570,7 +590,8 @@ + (void)handleAppIdChange:(NSString*)appId {
570590
// Drop cached identifiers — a real app-id change invalidates them.
571591
[OSResilientStorage setStrings:@{
572592
OSResilientStorage.keySubscriptionId: @"",
573-
OSResilientStorage.keyOneSignalId: @""
593+
OSResilientStorage.keyOneSignalId: @"",
594+
OSResilientStorage.keyReceiveReceiptsEnabled: @""
574595
}];
575596

576597
// Clear all cached data, does not start User Module nor call logout.

0 commit comments

Comments
 (0)