0
votes

I'm creating a property wrapper for UserDefaults.

What i'm trying to achieve is:

  • Setting a non-nil value to property will store it in User default.
  • Setting nil will remove the Object from UserDefault.

But below code throws compiler error:

Initializer for conditional binding must have Optional type, not 'T'

@propertyWrapper
struct UserDefault<T> {
    let key: String
    let defaultValue: T

    init(_ key: String, defaultValue: T) {
        self.key = key
        self.defaultValue = defaultValue
    }

    var wrappedValue: T {
        get { UserDefaults.standard.value(forKey: key) as? T ?? defaultValue }
        set {
            if let newValue = newValue {
                UserDefaults.standard.setValue(newValue, forKey: key)
            } else {
                UserDefaults.standard.removeObject(forKey: key)
            }
        }
    }
}

// Declaration
@UserDefault("key", defaultValue: nil)
static var newUserDefaultValue: String

Is there any way to identify T is optional? as I can remove the key from UserDefaults. If not how to achieve the desired output?

1
Have you tried setting T as T? as the type when initalizingOxthor
This is tricky, there is some possibility. It's a fact that your implementation will not work good for example because when T is optional then expression as? T returns T? so for concrete type you will have for example as? Bool? which is Bool??. This case is discussed in greater detail in the article dev.to/kodelit/…kodelit

1 Answers

0
votes

You have 2 problems in your code:

1) In your case, wrappedValue and defaultValue should be an optional:

struct UserDefault<T> {
  let key: String
  let defaultValue: T? // defaultValue should be optional

  // defaultValue should be optional
  init(_ key: String, defaultValue: T?) {
    self.key = key
    self.defaultValue = defaultValue
  }

  // wrappedValue should also be optional
  var wrappedValue: T? {
    get { UserDefaults.standard.value(forKey: key) as? T ?? defaultValue }
    set {
      if let newValue = newValue {
        UserDefaults.standard.setValue(newValue, forKey: key)
      } else {
        UserDefaults.standard.removeObject(forKey: key)
      }
    }
  }
}

2) If you initialize with nil defaultValue, you should specify type T for compiler:

// Type T should be expicitly specified. For example as String
let valueWithNilDefault = UserDefault<String>("key", defaultValue: nil)

// Type T will be determined during compile time as Int
let valueWithDefault = UserDefault("key", defaultValue: 15)