Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

willSet not being called on SwiftUI's new @AppStorage property

Tags:

ios

swift

swiftui

I am utilising SwfitUI's new @AppStorage property. However, the willSet property observer is not firing.

Here is my UserDefaults value codeResult changing from an asynchronous call:

class Main: ObservableObject {
    func code(data: [AnyHashable:Any?]){
        UserDefaults.standard.set("incorrect", forKey: "codeResult")
    }
}

And below in my View, willSet is not being called:

struct MainView: View {
    @ObservedObject var main = Main()
    @ObservedObject var mainCountdown: CountDown
    @ObservedObject var locationManager = LocationManager()
    @State private var keyboardHeight: CGFloat = 0
    @State var showCode: Bool = false
    @AppStorage("codeResult") var codeResult = UserDefaults.standard.string(forKey: "codeResult") ?? "" {
        willSet {
            print("willSet: \(newValue)") // doesn't fire 
        }
        didSet {
            print("didSet: \(oldValue)") // doesn't fire
        }
    }

Any idea why it's not firing?

like image 573
Zorgan Avatar asked Oct 27 '25 06:10

Zorgan


1 Answers

The willSet/didSet are observers for property change (not publisher observers). Thus to get into willSet/didSet you have to change property codeResult directly.

Here is a demo that depicts differences - both buttons update Text (because codeResult is dynamic observable property, but only direct assign to codeResult activates property willSet/didSet)

Tested with Xcode 12b3 / iOS 14.

struct DemoAppStorageDidSet: View {
    @AppStorage("codeResult") var codeResult: String = "Default" { // << default value
        willSet {
            print("willSet: \(newValue)")
        }
        didSet {
            print("didSet: \(oldValue)")
        }
    }

    var body: some View {
        VStack {
            Text("Code Result: \(codeResult)")
            Divider()
            Button("AppStore Update") {
                self.codeResult = "AppStore"   // << activates willSet/didSet !!
            }
            Divider()
            Button("UserDefaults Update") {
                UserDefaults.standard.set("UserDefaults", forKey: "codeResult")
            }
        }
    }
}

Note: you don't need to initialise AppStorage with UserDefaults value, it is read by default.

like image 124
Asperi Avatar answered Oct 29 '25 21:10

Asperi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!