setters method getters create and swift

method - Swift 4: getter y setter directamente en una estructura o clase



swift set getter and setter (4)

Además de todas las variantes propuestas, también puede definir su operador personalizado para asignar valor a la estructura DefaultsKey .

Para eso, su estructura DefaultsKey debe verse así:

struct DefaultsKey<ValueType> { private let key: String private let defaultValue: ValueType public init(_ key: String, defaultValue: ValueType) { self.key = key self.defaultValue = defaultValue } public private(set) var value: ValueType { get { let value = UserDefaults.standard.object(forKey: key) return value as? ValueType ?? defaultValue } set { UserDefaults.standard.setValue(newValue, forKey: key) } } }

Explicación para el bloque de código DefaultsKey :

  1. private(set) var significa que puede establecer el valor de esta propiedad solo donde puede acceder a ella con private nivel de acceso private (también puede escribir internal(set) o fileprivate(set) para poder establecerla desde niveles de acceso internal y de fileprivate en consecuencia).

    Tendrá que establecer la propiedad de value más adelante. Para acceder a este valor, el captador se define como public (escribiendo public antes de private(set) ).

  2. No es necesario que utilice el método synchronize() ("este método no es necesario y no debe usarse", consulte: https://developer.apple.com/documentation/foundation/userdefaults/1414005-synchronize ).

Ahora es el momento de definir un operador personalizado (puedes nombrarlo como quieras, esto es solo por ejemplo):

infix operator <<< extension DefaultsKey { static func <<<(left: inout DefaultsKey<ValueType>, right: ValueType) { left.value = right } }

Con este operador no podría establecer un valor de tipo incorrecto, por lo que es seguro para el tipo.

Para probar puedes usar un poco modificado tu código:

class AppUserDefaults { var username = DefaultsKey<String>("username", defaultValue: "Unknown") var age = DefaultsKey<Int?>("age", defaultValue: nil) } let props = AppUserDefaults() props.username <<< "bla" props.age <<< 21 props.username <<< "Yo man" print("username: /(props.username.value)") print("username: /(props.age.value)")

Espero eso ayude.

Estoy tratando de encontrar una manera de acceder a UserDefaults usando propiedades. Sin embargo, estoy atascado porque no puedo entender cómo hacer que las propiedades sean dinámicas, por así decirlo.

Esta es la idea genérica, esa API que quiero :

class AppUserDefaults { var username = DefaultsKey<String>("username", defaultValue: "Unknown") var age = DefaultsKey<Int?>("age", defaultValue: nil) } let props = AppUserDefaults() props.username = "bla" print("username: /(props.username)")

Pero eso (por supuesto) no funciona, ya que el tipo es DefaultsKey<String> , no String . Así que tengo que agregar una propiedad de valor a la implementación de DefaultsKey solo para agregar el getter y el setter, de esta manera:

struct DefaultsKey<ValueType> { private let key: String private let defaultValue: ValueType public init(_ key: String, defaultValue: ValueType) { self.key = key self.defaultValue = defaultValue } var value: ValueType { get { let value = UserDefaults.standard.object(forKey: key) return value as? ValueType ?? defaultValue } set { UserDefaults.standard.setValue(newValue, forKey: key) UserDefaults.standard.synchronize() } } }

y luego usarlo así:

let props = AppUserDefaults() props.username.value = "bla" print("username: /(props.username.value)")

Pero eso me parece bastante feo. También probé un método de subíndice, pero aún así debes agregar [] lugar de .value :

struct DefaultsKey<ValueType> { ... subscript() -> ValueType { get { let value = UserDefaults.standard.object(forKey: key) return value as? ValueType ?? defaultValue } set { UserDefaults.standard.setValue(newValue, forKey: key) UserDefaults.standard.synchronize() } } } let props = AppUserDefaults() props.username[] = "bla" print("username: /(props.username[])")

Básicamente, lo que quiero es que pueda definir el getter y el setter directamente en DefaultsKey lugar de tener que pasar por esa propiedad de value . ¿Esto simplemente no es posible con Swift? ¿Hay otra forma de obtener el comportamiento que deseo, donde las propiedades definidas en AppUserDefaults son "dinámicas" y pasan por un getter y setter, sin tener que definirlo en la declaración de propiedad dentro de AppUserDefaults ?

Espero estar usando los términos correctos aquí y aclarar la pregunta para todos.


Aquí es cómo lo estamos haciendo en mi proyecto actual. Es similar a algunas otras respuestas, pero creo que incluso menos placa de la caldera. Usando isFirstLaunch como ejemplo.

enum UserDafaultsKeys: String { case isFirstLaunch } extension UserDefaults { var isFirstLaunch: Bool { set { set(!newValue, forKey: UserDafaultsKeys.isFirstLaunch.rawValue) synchronize() } get { return !bool(forKey: UserDafaultsKeys.isFirstLaunch.rawValue) } } }

Entonces se usa así.

UserDefaults.standard.isFirstLaunch = true

Tiene los beneficios de no necesitar aprender un objeto separado para usar y es muy claro dónde se almacena la variable.


Hay una muy buena publicación en el blog de radex.io sobre NSUserDefaults de tipo estático que podría resultarle útil si entiendo lo que está intentando hacer.

Lo actualicé para el último Swift (ya que ahora tenemos conformidad condicional) y agregué un valor predeterminado para mi implementación, que he establecido a continuación.

class DefaultsKeys {} class DefaultsKey<T>: DefaultsKeys { let key: String let defaultResult: T init (_ key: String, default defaultResult: T) { self.key = key self.defaultResult = defaultResult } } extension UserDefaults { subscript<T>(key: DefaultsKey<T>) -> T { get { return object(forKey: key.key) as? T ?? key.defaultResult } set { set(newValue, forKey: key.key) } } } // usage - setting up the keys extension DefaultsKeys { static let baseCurrencyKey = DefaultsKey<String>("Base Currency", default: Locale.current.currencyCode!) static let archiveFrequencyKey = DefaultsKey<Int>("Archive Frequency", default: 30) static let usePasswordKey = DefaultsKey<Bool>("Use Password", default: false) } // usage - getting and setting a value let uDefaults = UserDefaults.standard uDefaults[.baseCurrencyKey] = "GBP" let currency = uDefaults[.baseCurrencyKey]


Lo único que puedo pensar es esto:

struct DefaultsKey<ValueType> { private let key: String private let defaultValue: ValueType public init(_ key: String, defaultValue: ValueType) { self.key = key self.defaultValue = defaultValue } var value: ValueType { get { let value = UserDefaults.standard.object(forKey: key) return value as? ValueType ?? defaultValue } set { UserDefaults.standard.setValue(newValue, forKey: key) UserDefaults.standard.synchronize() } } } class AppUserDefaults { private var _username = DefaultsKey<String>("username", defaultValue: "Unknown") var username: String { set { _username.value = newValue }, get { return _username.value } } } let props = AppUserDefaults() props.username = "bla" print("username: /(props.username)")