SwiftUI View Property willSet & didSet property observers not working
EDIT:
On iOS 14 property observers work the same as they did in iOS 13. But, we now have the .onChange(of:perform:)
as a replacement. Docs
Text(self.myString).onChange(of: self.myString) { newValue in print("myString changed to: \(newValue)") }
Property observers on basic var
s technically work in SwiftUI. If you do something like var view = MyTestView(...)
and then view.FSAC = "updated"
the the observers will fire (I've verified this).
However, typically with SwiftUI you construct the View
(which is a struct not a class) within body
during each layout pass. In your case var body: some View { MyTestView(FSAC: "FSAC Value") }
.
Property observers do not fire during init, and therefore they aren't usually useful in SwiftUI View
s.
If you would like to update some sort of State during init, take a look at this answer.
SwiftUI var: willSet, didSet
@Published
automatically publishes when the property is at willSet
https://developer.apple.com/documentation/combine/published
If you prefer to publish during didSet
remove @Published
and publish manually.
import SwiftUI
class ManualOObject: ObservableObject {
var someValue: Int = 0{
willSet{
print("willSet - \(someValue)")
}
didSet{
print("didSet - \(someValue)")
objectWillChange.send()
}
}
init() {
for n in 0...5 {
DispatchQueue.main.asyncAfter(deadline: .now() + Double(n)) {
print("n = \(n.description)")
self.someValue = n
}
}
}
}
struct ManualOOView: View {
@StateObject var manualOObject = ManualOObject()
var body: some View {
Text(manualOObject.someValue.description).onReceive(manualOObject.objectWillChange, perform: { _ in
print("UI received = \(manualOObject.someValue)")
})
}
}
Struct not calling property observers on new object
You can construct a custom initializer as follows.
struct MyStruct {
var myValue: Int {
willSet {
print("willSet")
}
didSet {
print("didSet")
}
}
init(value: Int) {
myValue = value
// print something here
}
}
var abc = MyStruct(value: 3)
abc.myValue = 5
SwiftUI - is it possible to get didSet to fire when changing a @Published struct?
The property observer observes the property. The trouble goes from new Swift syntax related to property wrappers. In your case you try to observe if value of Published (which is a struct defining the specialized property wrapper) did change, not the value of the wrapped property.
If you need to monitor left or right values in PaddingRect, simply observe this values directly.
import SwiftUI
struct PaddingRect {
var left: CGFloat = 20 {
didSet {
print("left padding change from:", oldValue, "to:", left)
}
}
var right: CGFloat = 20 {
didSet {
print("right padding change from:", oldValue, "to:", right)
}
}
}
final class SomeStore : ObservableObject {
@Published var someOtherValue: String = "Waiting for didSet"
@Published var paddingRect:PaddingRect = PaddingRect()
}
struct ContentView: View {
@ObservedObject var store = SomeStore()
var body: some View {
VStack {
Spacer()
Rectangle()
.fill(Color.yellow)
.padding(.leading, store.paddingRect.left)
.padding(.trailing, store.paddingRect.right)
.frame(height: 100)
Text(store.someOtherValue)
HStack {
Button(action: {
// This doesn't call didSet
self.store.paddingRect.left += 20
// This does call didSet, ie. setting the whole thing
self.store.paddingRect = PaddingRect(
left: self.store.paddingRect.left + 20,
right: self.store.paddingRect.right
)
}) {
Text("Padding left +20")
}
Button(action: {
self.store.paddingRect.right += 20
}) {
Text("Padding right +20")
}
}
Spacer()
}
}
}
struct ContentView_Preview: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Or take the advantage that Published projected value is Publisher and aply next modifier to any View
.onReceive(store.$paddingRect) { (p) in
print(p)
}
Property observers willSet and didSet; Property getters and setters
When and why should I use willSet/didSet
willSet
is called just before the value is stored.didSet
is called immediately after the new value is stored.
Consider your example with outputs:
var variable1 : Int = 0 {
didSet{
print("didSet called")
}
willSet(newValue){
print("willSet called")
}
}
print("we are going to add 3")
variable1 = 3
print("we added 3")
Output:
we are going to add 3
willSet called
didSet called
we added 3
it works like pre/post -condition
On the other hand, you can use get
if you want to add, for example, a read-only property:
var value : Int {
get {
return 34
}
}
print(value)
value = 2 // error: cannot assign to a get-only property 'value'
Related Topics
Why Doesn't Swift Force My Designated Initializer to Call Super
How to Capitalize First Word in Every Sentence with Swift
Realitykit - How to Add a Video Material to a Modelentity
Obtain Nsurl from Uiimagepickercontroller
How to Compare Range<String.Index> and Defaultbidirectionalindices<String.Characterview>
Writing Data to an Outputstream with Swift 5+
How to Fix Memory Leaks in iOS Applications
How to Replicate Hash_Hmac('Sha256', $Key, $Secret_Key) Function in Swift 4
Implement Protocol Through Extension
Animate an Skspritenode with Textures That Have a Size Different from the Original
Split Paragraphs into Sentences
If-Let Any to Rawrepresentable<String>
Saving Dictionary into Nsuserdefaults
Querying in Firebase by Child of Child
How to Create a Portal Effect in Arkit Just Using the Scenekit Editor