Как интегрировать перечисления в propertyWrapped UserDefault

#swift #enums #property-wrapper

#swift #перечисления #свойство-оболочка

Вопрос:

Приведенная ниже структура будет работать для примитивных типов данных, таких как Int , String , Double , и т.д., Есть ли способ заставить это работать для перечислений, чтобы мне не приходилось использовать rawValues для разбора вручную?

 @propertyWrapper
struct Storage<T> {
  let objectName: String
  let defaultValue: T
  let defaults: UserDefaults

  init(_ objectName: String, defaultValue: T, defaults: UserDefaults = .standard) {
    self.objectName = objectName
    self.defaultValue = defaultValue
    self.defaults = defaults
  }

  var wrappedValue: T {
    get { return self.defaults.object(forKey: self.objectName) as? T ?? self.defaultValue }
    set { self.defaults.set(newValue, forKey: self.objectName) }
  }
}
  

В настоящее время мой обходной путь заключается в том, чтобы обернуть объект в другой метод получения и установки для перечисления следующим образом.

 enum SomeType: String {
  case foo, bar, baz
}


class Defaults {
   @Storage("object", default: "")
   private var: objectContainer: String

   var object: SomeType? {
      get {
         return SomeType(rawValue: self.objectContainer)
      }
      set {
         self.objectContainer = newValue.rawValue ?? ""
      }
   }
}
  

Я попытался создать другую оболочку свойств, специфичную для RawRepresentable типов, но Swift не может определить тип T из этого.

 @propertyWrapper
struct RawRepresentableStorage<T: RawRepresentable> {
  let objectName: String
  let defaultValue: T
  let defaults: UserDefaults
  
  init(_ objectName: String, defaultValue: T, defaults: UserDefaults = .standard) {
    self.objectName = objectName
    self.defaultValue = defaultValue
    self.defaults = defaults
  }
  
  var wrappedValue: T {
    get {
      guard let object = self.defaults.object(forKey: self.objectName) as? T else {
        return self.defaultValue
      }
      
      // Error on this line:
      // `Cannot convert value of type 'T' to expected argument type 'T.RawValue'`
      return T(rawValue: object) ?? self.defaultValue 
    }
    set {
      self.defaults.set(newValue.rawValue, forKey: self.objectName)
    }
  }
}
  

Также было бы не идеально создавать типы оболочек для каждого сохраняемого перечисления в базе кода, даже если их всего несколько

Есть ли что-то, чего мне не хватает, или это текущее ограничение Swift?

Ответ №1:

В вашем wrappedValue получателе вы приводите к неправильному типу. Вы переходите object к T(rawValue:) , поэтому его тип должен быть T.RawValue , а не T .

 @propertyWrapper
struct RawRepresentableStorage<T: RawRepresentable> {
    let objectName: String
    let defaultValue: T
    let defaults: UserDefaults
    
    init(_ objectName: String, defaultValue: T, defaults: UserDefaults = .standard) {
        self.objectName = objectName
        self.defaultValue = defaultValue
        self.defaults = defaults
    }
    
    var wrappedValue: T {
        get {
            guard let object = self.defaults.object(forKey: self.objectName) as? T.RawValue else {
                return self.defaultValue
            }
            
            return T(rawValue: object) ?? self.defaultValue
        }
        set {
            self.defaults.set(newValue.rawValue, forKey: self.objectName)
        }
    }
}
  

Комментарии:

1. Ах да, но все T(rawValue: object) равно не будет компилироваться, потому что компилятор не знает общий тип T . Я бы увидел это, если бы запустил код, но спасибо за предупреждение.

2. @ZonilyJame код в моем ответе компилируется просто отлично, я протестировал его на игровой площадке. Компилятор может вывести тип T из объявления свойства, которое вы аннотируете @RawRepresentableStorage . Какую ошибку компилятора вы получаете? И как вы используете оболочку свойств, которая выдает указанную ошибку?