0

I have the following property wrapper to store my values:

@propertyWrapper struct UserDefault<T: Codable> {
    var key: String
    var wrappedValue: T? {
        get {
            if let data = UserDefaults.standard.object(forKey: key) as? Data {
                return try? JSONDecoder().decode(T.self, from: data)
            }
            return nil
        }
        set {
            if let encoded = try? JSONEncoder().encode(newValue) {
                UserDefaults.standard.set(encoded, forKey: key)
            }
        }
    }
}

I store my values in the following Class:

class AppState: ObservableObject {
    @UserDefault(key: "achievements") var achievements: [String : Bool]?
}

In my Struct I try to read values, but I always get nil. What am I missing?

struct XXX {
   AppState().achievements             = [:]
   AppState().achievements?["Normal"] = true
   print("ACHIEVEMENTS")
   print(AppState().achievements)
}

With a ? I get nil, when I try to unwrap this value the app crashes. What do I need to do to write and read from my achievement variable?

Bradley Mackey
  • 6,777
  • 5
  • 31
  • 45
Nathanael Tse
  • 171
  • 13
  • I have to go through it in more details, I just saw it was handling Arrays and not dictionaries, but I might just make an array of a Struct, which should work with the suggested approach. Here, I guess I am just not able to read from the AppState directly. In a normal dictionary I would do it like this: **var text : [String:Bool] = [:] text["hallo"] = true** but the app state cannot be written or read. – Nathanael Tse Jan 17 '23 at 15:19

1 Answers1

1

Aside from the structural issues (you appear to be putting bare expressions in the body of a struct, when these should be in a func), the issue comes down to not referring to the same instance of AppState when creating the achievements initially.

Everytime you call AppState(), you're creating a new instance.

func testWithProblem() {
   // Creating an instance of AppState and setting achievements...
   AppState().achievements = [:]
   // Creating another instance! The first instance is now gone from memory.
   // achievements on this instance is nil, so the next line has no effect.
   AppState().achievements?["Normal"] = true
   print("ACHIEVEMENTS")
   // Yet another instance...
   print(AppState().achievements)
}

You make sure you're referring to the same instance.

func testItWorksNow() {
   // You need to use the same instance...
   let appState = AppState()
   appState.achievements = [:]
   // So this line will refer to the same achievements dict as you've just created.
   appState.achievements?["Normal"] = true
   print("ACHIEVEMENTS")
   print(appState.achievements)
}
Bradley Mackey
  • 6,777
  • 5
  • 31
  • 45
  • Thank you! I assumed that if a Class is defined no new instances are generated compared to a Struct. I switched over to use @AppStrorage, which is easier to handle! – Nathanael Tse Jan 18 '23 at 06:36