11import BitwardenKit
2- import CoreData
2+ @ preconcurrency import CoreData
33
44// MARK: - AuthenticatorStoreType
55
@@ -20,8 +20,11 @@ public enum AuthenticatorBridgeStoreType {
2020private let authenticatorBridgeModelName = " Bitwarden-Authenticator "
2121
2222/// A data store that manages persisting data across app launches in Core Data.
23+ /// This is currently marked `@unchecked Sendable` because of how we ensure thread safety of the `backgroundContext`
24+ /// property. Once we have a minimum version of iOS 16 or higher, we can migrate to the `Synchronization` framework
25+ /// and make this more properly `Sendable`.
2326///
24- public class AuthenticatorBridgeDataStore {
27+ public final nonisolated class AuthenticatorBridgeDataStore : @ unchecked Sendable {
2528 // MARK: Type Properties
2629
2730 /// The managed object model representing the entities in the database schema. CoreData throws
@@ -41,12 +44,29 @@ public class AuthenticatorBridgeDataStore {
4144
4245 // MARK: Properties
4346
47+ /// A thread-safe lock for `backgroundContext`. Once we have a minimum of iOS 16, we can use an
48+ /// `OSAllocatedUnfairLock` instead.
49+ private let _backgroundContextLock = DispatchQueue ( label: " backgroundContext.lock " )
50+
51+ /// A private backing for `backgroundContext`. The `backgroundContext` variable provides thread-safe access, and
52+ /// is what should be used. Once we have a minimum of iOS 16, this can be converted to an `OSAllocatedUnfairLock`,
53+ /// and remove the need for the additional `_backgroundContextLock`.
54+ private var _backgroundContext : NSManagedObjectContext ?
55+
4456 /// A managed object context which executes on a background queue.
45- private( set) lazy var backgroundContext : NSManagedObjectContext = {
46- let context = persistentContainer. newBackgroundContext ( )
47- context. mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
48- return context
49- } ( )
57+ /// This is the thread-safe version of the backing variable `_backgroundContext`,
58+ /// and initializes that property lazily.
59+ public var backgroundContext : NSManagedObjectContext {
60+ _backgroundContextLock. sync {
61+ if let context = _backgroundContext {
62+ return context
63+ }
64+ let newContext = persistentContainer. newBackgroundContext ( )
65+ newContext. mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
66+ _backgroundContext = newContext
67+ return newContext
68+ }
69+ }
5070
5171 /// The service used by the application to report non-fatal errors.
5272 let errorReporter : ErrorReporter
0 commit comments