How might I get didSet behaviour on an AppStorage var?

I've defined a value stored in UserDefaults. In a view struct I have code that can successfully update the stored value. I've also added an @AppStorage var in an instance of a class, that can read this value and run business logic that depends on the current stored value.

But what I really want to do, is have code in my class that gets automatically called when the value stored in UserDefaults gets updated. Basically I want to do this:

    @AppStorage("languageChoice") var languageChoice: LanguageChoice = .all {
        didSet {
            print("hello")
        }
    }

Unfortunately didSet closures in @AppStorage vars do not appear to get called :-(

My clumsy attempts to use combine have all ended in tears from the compiler.

Any/all suggestions are greatly appreciated. thanks, Mike

Answered by mikeTheDad in 789501022

I came up with an implementation that mostly meets my needs. I've described it here: https://developer.apple.com/forums/thread/756192

Surprising.

I tested the code proposed here and it works: https://stackoverflow.com/questions/63171806/willset-not-being-called-on-swiftuis-new-appstorage-property

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

 

Tapping "AppStore Update" logs as expected:

willSet: AppStore
didSet: Default

Tapping a second time:

willSet: AppStore
didSet: AppStore

So please show your complete code that does not work (maybe stupid question: are you sure the console is visible for log ?)

Hi again @Claude31 turns out I was curious enough about this to make time to investigate it sooner. In my sample code, neither the View's willSet/didSet nor the class's willSet/didSet are getting called.

import SwiftUI

struct ContentView: View {
    @AppStorage("extended") var extended: Bool = true {
        willSet {
            print("ConteentView willSet")
        }
        didSet {
            print("ContentView didSet")
        }
    }
    
    var body: some View {
        VStack {
            Toggle("Extended", isOn: $extended)
            Text(LanguageManager.shared.summary)
        }
        .padding()
    }
}

#Preview {
    ContentView()
}

class LanguageManager {
    static let shared = LanguageManager()
    var items: [String] = ["basic", "list"]
    var summary: String {
        return items.reduce("") {
            return "\($0)\n\($1)"
        }
    }
                
    @AppStorage("extended") var extended: Bool = true {
        willSet {
            print("LanguageManager willSet")
        }
        didSet {
            print("LanguageManager didSet")
            updateItems()
        }
    }
    func updateItems() {
        if extended {
            items = ["much", "more", "than", "basic", "list"]
        } else {
            items = ["basic", "list"]
        }
    }
}

** Update**: I now see I should have followed the link you included. Sorry about not doing that before answering. However this answer suggests that when a user updates the UI, the @AppStorage var in my class will most definitely not be getting updated. And that is the one that I'm hoping will be able to be notified when 'somebody else' updates one of the UserDefault values. I want to define multiple UserDefault values that can be updated via settings/prefs UI. and then when any of these values change, LanguageManager will be notified (pub/sub?) of the change, and run updateItems()

Accepted Answer

I came up with an implementation that mostly meets my needs. I've described it here: https://developer.apple.com/forums/thread/756192

How might I get didSet behaviour on an AppStorage var?
 
 
Q