Swiftui View Property Willset & Didset Property Observers Not Working

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 vars 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 Views.

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()
}
}

Sample Image

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



Leave a reply



Submit