Files
purchases-ios/Sources/Misc/SynchronizedUserDefaults.swift
T
NachoSoto 9f7632178d DeviceCache: Sendable conformance and fixed thread-safety (#1823)
For [CSDK-379].
Extracted #1795 to make it easier to review this fix in isolation.

`handleUserDefaultsChanged` isn't guaranteed to be called from any particular thread, so its access to `appUserIDHasBeenSet` wasn't thread-safe.

This is now guaranteed by `Sendable`.

_In theory. In practice we are required to use `@unchecked` for now because of `NotificationCenter` and the class not being `final`. But without those I verified that the class does compile with `Sendable`, which means that there aren't any other potential issues._

[CSDK-379]: https://revenuecats.atlassian.net/browse/CSDK-379?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2022-08-15 11:04:48 -07:00

50 lines
1.5 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
//
// SynchronizedUserDefaults.swift
//
// Created by Nacho Soto on 11/20/21.
import Foundation
/// A `UserDefaults` wrapper to synchronize access and writes.
///
/// - SeeAlso: `Atomic`.
internal final class SynchronizedUserDefaults {
private let atomic: Atomic<UserDefaults>
init(userDefaults: UserDefaults) {
self.atomic = .init(userDefaults)
}
func read<T>(_ action: (UserDefaults) throws -> T) rethrows -> T {
return try self.atomic.withValue {
return try action($0)
}
}
func write(_ action: (UserDefaults) throws -> Void) rethrows {
return try self.atomic.withValue {
try action($0)
// While Apple states `this method is unnecessary and shouldn't be used`
// https://developer.apple.com/documentation/foundation/userdefaults/1414005-synchronize
// It didn't become unnecessary until iOS 12 and macOS 10.14 (Mojave):
// https://developer.apple.com/documentation/macos-release-notes/foundation-release-notes
// there are reports it is still needed if you save to defaults then immediately kill the app.
// Also, it has not been marked deprecated... yet.
$0.synchronize()
}
}
}
extension SynchronizedUserDefaults: Sendable {}