I'm attempting to create a custom property wrapper supported by SwiftUI, meaning that changes to the corresponding properties values would cause an update to the SwiftUI view. Here is a simplified version of what I have:
@propertyWrapper
public struct Foo: DynamicProperty {
@ObservedObject var observed: SomeObservedObject
public var wrappedValue: [SomeValue] {
return observed.value
}
}
I see that even if my ObservedObject
is contained inside of my custom property wrapper, SwiftUI still catches the changes to SomeObservedObject
as long as:
DynamicProperty
Unfortunately the docs are sparse and I have a hard time telling if this only works out of luck with the current SwiftUI implementation.
The docs of DynamicProperty
(within Xcode, not online) seem to indicate that such a property is a property that is changed from the outside causing the view to redraw, but there is no guarantee about what happens when you conform your own types to this protocol.
Can I expect this to continue working in future SwiftUI releases?
Ok... here is alternate approach to get similar thing... but as struct only DynamicProperty
wrapped around @State
(to force view refresh).
It is simple wrapper but gives possibility to incapsulate any custom calculations with following view refresh... and as said using value-only types.
Here is demo (tested with Xcode 11.2 / iOS 13.2):
Here is code:
import SwiftUI
@propertyWrapper
struct Refreshing<Value> : DynamicProperty {
let storage: State<Value>
init(wrappedValue value: Value) {
self.storage = State<Value>(initialValue: value)
}
public var wrappedValue: Value {
get { storage.wrappedValue }
nonmutating set { self.process(newValue) }
}
public var projectedValue: Binding<Value> {
storage.projectedValue
}
private func process(_ value: Value) {
// do some something here or in background queue
DispatchQueue.main.async {
self.storage.wrappedValue = value
}
}
}
struct TestPropertyWrapper: View {
@Refreshing var counter: Int = 1
var body: some View {
VStack {
Text("Value: \(counter)")
Divider()
Button("Increase") {
self.counter += 1
}
}
}
}
For the question in your title, no. For example, here is some code that doesn't work:
class Storage {
var value = 0
}
@propertyWrapper
struct MyState: DynamicProperty {
var storage = Storage()
var wrappedValue: Int {
get { storage.value }
nonmutating set {
storage.value = newValue // This doesn't work
}
}
}
So apparently you still need to put a State
or ObservedObject
etc. inside your DynamicProperty
to trigger an update as Asperi did, a DynamicProperty
itself doesn't enforce any update.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With