@AppStorage
is a property wrapper for UserDefaults, provided by SwiftUI framework. It is very simple to use, but in practice, some simple things are not obvious.
I had a hiccup when I try to “observe” an AppStorage
using Combine. Let’s use this as an example:
@AppStorage("multiCamEnabled") var multiCamEnabled = true
If I try to observe with $multiCamEnabled.sink(...)
, that is not possible because $multiCamEnabled
is a Binding
, not a Publisher
(if it is a @Published
, then will be fine).
How to observe?
One solution is to use SwiftUI onChange
modifier.
Text("Any view")
.onChange(of: multiCamEnabled) {
...
}
But onChange
is limited to SwiftUI. What if you want to observe outside of the view eg. in a view model?
How to observe outside a View?
There is no way to observe an AppStorage/Binding directly. If you know, tell me. 🙏🏻
A workaround is to fallback on UserDefaults
, which we know very well how to observe one.
UserDefaults.standard.publisher(for: \.multiCamEnabled)
.sink { ... }
Note that UserDefaults need to be setup like this in order to use publisher(for:)
:
extension UserDefaults {
@objc var multiCamEnabled: Bool {
get { bool(forKey: .multiCamEnabled) }
set { set(newValue, forKey: .multiCamEnabled) }
}
}
/// Avoid repeating the String
extension String {
static let multiCamEnabled = "multiCamEnabled"
}
AppStorage uses UserDefaults
That is possible because AppStorage is simply using UserDefaults.
By default, it is using standard
. But that can be changed with the store
.
@AppStorage(.multiCamEnabled, store: UserDefaults.init(suiteName: "Shared")) var multiCamEnabled = true
Conclusion
SwiftUI is indeed re-learning everything.
AppStorage is new, yet I’m not entirely sure it is better.