mirror of
https://github.com/RevenueCat/purchases-ios
synced 2026-11-10 22:45:55 +05:45
Fixes [CSDK-16]. See https://docs.google.com/document/d/1DDyed98RFpIvUwUPJcetc7X4fBhDzf-r1trC4g3zf4c/edit# for reasoning and documentation. ### Motivation The RevenueCat SDK has always used `UserDefaults.standard` as the default value. The advantage of this was simplicity, however, this brings a few challenges. ### Issues - While `UserDefaults` is itself thread-safe, our users might be observing `UserDefaults.defaults` changes via `KVO`, `NotificationCenter`, or `@AppStorage` (see https://github.com/RevenueCat/purchases-ios/issues/1527). We update `UserDefaults` from background threads, and users might be getting updates on those threads without handling thread-safety correctly. - Users can accidentally delete data from the shared `UserDefaults` (see https://rev.cat/userdefaults-crash). Using a custom `UserDefaults` solves all these issues. ### Change The new `UserDefaults.revenueCatSuite` is used when: - Only if users aren’t overriding the `UserDefaults` parameter - Only if `UserDefaults.standard` has no value for `com.revenuecat.userdefaults.appUserID.new` (the user hasn't used the SDK in that app before). #### Logged during app start: > PurchaseTester[35110:6323140] DEBUG: ℹ️ Configuring SDK using RevenueCat's UserDefaults suite. ### Alternatives Considered See https://docs.google.com/document/d/1c2B4MNAFKs_DcFJ3OE_KnmryHCArBkjlZbAD9_kSXu0/edit# We considered also migrating data for everyone into the new `UserDefaults`. This new approach is much simpler, less error prone, easier to implement, and maintain. The only downside is that existing users won’t benefit from this change, but @aboedo correctly pointed out: if the app is working for them, no reason to change anything. So in this way, this becomes a purely forward-looking improvement. [CSDK-16]: https://revenuecats.atlassian.net/browse/CSDK-16?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
50 lines
1.8 KiB
Swift
50 lines
1.8 KiB
Swift
//
|
||
// Copyright RevenueCat Inc. All Rights Reserved.
|
||
//
|
||
// Licensed under the MIT License (the "License");
|
||
// you may not use this file except in compliance with the License.
|
||
// You may obtain a copy of the License at
|
||
//
|
||
// https://opensource.org/licenses/MIT
|
||
//
|
||
// UserDefaults+Extensions.swift
|
||
//
|
||
// Created by Nacho Soto on 11/9/22.
|
||
|
||
import Foundation
|
||
|
||
extension UserDefaults {
|
||
|
||
// These are the only 2 documented reasons why `.init(suiteName:)` might return `nil`:
|
||
// - "Because a suite manages the defaults of a specified app group, a suite name
|
||
// must be distinct from your app’s main bundle identifier.
|
||
// - The globalDomain is also an invalid suite name, because it isn't writeable by apps.
|
||
//
|
||
// Because we know at compile time that it's neither of those, this is a safe force-unwrap.
|
||
static let revenueCatSuite: UserDefaults = .init(suiteName: UserDefaults.revenueCatSuiteName)!
|
||
|
||
private static let revenueCatSuiteName = "com.revenuecat.user_defaults"
|
||
|
||
}
|
||
|
||
extension UserDefaults {
|
||
|
||
/// Determines the "default" `UserDefaults` to use for the SDK.
|
||
///
|
||
/// Moving foward, this default is `UserDefaults.revenueCatSuite`,
|
||
/// but existing users will continue using `UserDefaults.standard` for compatibility.
|
||
/// This is determined by the presence of an app user ID in `UserDefaults.standard`.
|
||
static func computeDefault() -> UserDefaults {
|
||
let standard: UserDefaults = .standard
|
||
|
||
if standard.value(forKey: DeviceCache.CacheKeys.appUserDefaults.rawValue) != nil {
|
||
Logger.debug(Strings.configure.using_user_defaults_standard)
|
||
return standard
|
||
} else {
|
||
Logger.debug(Strings.configure.using_user_defaults_suite_name)
|
||
return .revenueCatSuite
|
||
}
|
||
}
|
||
|
||
}
|