Edit: There is no need to compose @Published @AppStorage("persisted")
because @AppStorage
does "publish" its change and can be used inside ObservableObject
and work just like @Published
.
There is DefaultsWrapper package: a replacement of @AppStorage
: it support many more types like Decimal
, UUID
. It's extensible, too!
Original post:
final class PublishedAppStorage: ObservableObject {
@Published @AppStorage("persisted") var data = 0
}
when compile get this error in the error navigator pane:
Key path value type 'Int' cannot be converted to contextual type 'AppStorage'
same error when change to this:
final class PublishedAppStorage: ObservableObject {
@Published<AppStorage<Int>> @AppStorage("persisted") var data = 0
}
I've never seen compile error like this in Xcode: no parse error but compile result in error in the error pane, not at the code site. (Edit: I think I understand why: the error occur inside the property wrapper, somehow the @SomePW compiler machinery cannot point the error site back at the call site)
Is there anyway to compose these two property wrappers?
My workaround:
// have to use this separate enum
enum PersistStore {
// cannot put this in `PublishedAppStorage` and used directly in there
@AppStorage("persisted") static var persisted = 0
}
final class PublishedAppStorage: ObservableObject {
// this do not compile
// @Published<AppStorage<Int>> @AppStorage("persisted") var data = 0
@Published var data = PersistStore.persisted {
didSet {
PersistStore.persisted = data
}
}
}
These are all boilerplate, is it possible to just make a @PublishedAppStorage property wrapper have the functionality of a @Published that save change to AppStorage
?, provide two projectedValue: one a Publisher
, the other a SwiftUI.Binding<Value>
...
Full test case
import SwiftUI
final class PublishedAppStorage: ObservableObject {
@Published @AppStorage("persisted") var data = 0
}
struct ContentView: View {
@EnvironmentObject var data: PublishedAppStorage
var body: some View {
VStack {
Text("Hello, world! data = \(data.data)")
.padding()
TextField("Enter", value: $data.data, format: .number, prompt: Text("Value"))
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}