Home > Software design >  Swift: Loading and Saving with @propertyWrapper and an [String : Bool]
Swift: Loading and Saving with @propertyWrapper and an [String : Bool]

Time:01-18

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?

CodePudding user response:

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)
}
  • Related