Hey,
I am just about to prepare my app for Swift 6, and facing the issue that UserDefaults is not Sendable. The documentation states that its thread safe, so I am wondering, why is it not marked as Sendable? Was it just forgotten? Is it safe to mark it as nonisolated(unsafe)
or @unchecked Sendable
?
if an open class would be marked Sendable and someone tried to subclass it, swift would emit a warning that the subclass must also be Sendable.
Right. As long as the subclass was written in Swift. And you actually compiled it from source, rather then importing the implementation from a complied binary. Like, say, one of the frameworks built in to an Apple OS (-:
So, there’s two parts to this:
-
Why isn’t
UserDefaults
sendable? -
What should you do in your code?
I’ll tackle these in order.
I usually don’t answer why questions here on DevForums [1]. However, in this case Foundation is part of the Swift project and so I have a little more latitude. Specifically, check out Sendable in Foundation on Swift Forums.
No one from the Foundation team has chimed in there, but someone posted a bug number (FB11224364
). I followed that link and, again, I’m bumping into the limit of what I can talk about publicly but, it seems that subclassing is the primary concern.
As to what you can do about it, the obvious solution is to create a new UserDefaults
object in every context that needs it. That’s guaranteed to work regardless of UserDefaults
subclasses [2].
If you don’t like that approach you can use some alternative that opts out of strict concurrency checking. For example, the property wrapper example you posted compiles just fine if you deploy nonisolated(unsafe)
on the MyAppGroup.defaults
global.
enum MyAppGroup {
nonisolated(unsafe) static let defaults: UserDefaults = { UserDefaults(suiteName: "com.example.waffle-varnish")! }()
}
ps Your exact code doesn’t compile because userTimezoneId
itself is a static variable, but that’s a different problem.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] See tip 3 in Quinn’s Top Ten DevForums Tips.
[2] Because the obvious implementation is something like this:
let suiteName: String? = …
let defaults = suiteName.map { UserDefaults(suiteName: $0)! } ?? .standard
which means you can only create standard user defaults values.