I've written a lot of UserDefaults related stuff in this class, and I'm using authToken
as an example here. authToken
is referenced in prepare
the synchronised nonisolated protocol-method of the web library Moya
.
I would like to know if this approach is really secure and reliable. I would appreciate for any help!
public final class AppDefaults: ObservableObject {
public static var shared: AppDefaults {
queue.sync {
singleton
}
}
nonisolated(unsafe) private static let singleton = AppDefaults()
private init() {}
private static let queue = DispatchQueue(label: "xxx.yyyy.zzzz.AppDefaults", qos: .userInitiated)
@AppStorage(UserDefaults.authToken) var authToken: String = ""
}
statics are automatically backed by dispatch_once()
, therefore you can simply do:
public final class AppDefaults {
public static let shared = AppDefaults()
}
You also probably want to mark this class as Sendable
.
1 Like
Thanks for your reply! Actually this class is a Non-Sendable class, which also can't be isolated to MainActor
due to usage constraints, so I'd like to manually ensure thread-safety for it.
Yes you can manually ensure thread-safety, for example using a Mutex
, and still mark the class Sendable
. The compiler knows about Mutex
and makes this possible.
Or if you are using other synchronization primitives such as dispatch queues, you can still mark the class @unchecked Sendable
.
public static var shared: AppDefaults {
queue.sync {
let lock = NSLock()
lock.lock(); defer { lock.unlock() }
return singleton
}
}
Thank you! NSLock
can be used in my project env, under iOS 17.x.
Reread my first answer, you do not need all this.
public final class AppDefaults: ObservableObject, @unchecked Sendable {
public static let shared = AppDefaults()
private init() {}
private let queue = DispatchQueue(label: "xxx.yyyy.zzzz.AppDefaults", qos: .userInitiated)
@AppStorage(UserDefaults.wepediasKeyAuthToken) private var dq_authToken: String = ""
var authToken: String {
get {
queue.sync { dq_authToken }
}
set {
queue.async { [self] in
dq_authToken = newValue
}
}
}
}
I ended up with a lot more lines of code, but the effort of migrating to Swift 6 will all be worth it! Thank you for your help!