Necesito rastrear la actualización en una variable de tipo estructura. ¿Es posible agregar un observador en la variable de estructura en Swift?
Ejemplo:
struct MyCustomStruct { var error:Error? var someVar:String? } class MyClass{ var myCustomStruct:MyCustomStruct? } Quiero agregar un observador en la variable myCustomStruct .
Los " observadores de propiedades " estándar de Swift ( didSet y willSet ) están diseñados para permitir que un tipo observe los cambios en sus propias propiedades, pero no para permitir que los objetos externos agreguen sus propios observadores. Y KVO, que admite observadores externos, es solo para subclases NSObject de propiedades dynamic y @objc (como se describe en Uso de la observación de valores clave en Swift ).
Entonces, si desea que un objeto externo observe los cambios dentro de una struct , como han señalado otros, debe crear su propio mecanismo de observación utilizando Swift didSet y similares. Pero en lugar de implementarlo usted mismo, propiedad por propiedad, puede escribir un tipo genérico para que lo haga por usted. P.ej,
struct Observable<T> { typealias Observer = String private var handlers: [Observer: (T) -> Void] = [:] var value: T { didSet { handlers.forEach { $0.value(value) } } } init(_ value: T) { self.value = value } @discardableResult mutating func observeNext(_ handler: @escaping (T) -> Void) -> Observer { let key = UUID().uuidString as Observer handlers[key] = handler return key } mutating func remove(_ key: Observer) { handlers.removeValue(forKey: key) } }Entonces puedes hacer cosas como:
struct Foo { var i: Observable<Int> var text: Observable<String> init(i: Int, text: String) { self.i = Observable(i) self.text = Observable(text) } } class MyClass { var foo: Foo init() { foo = Foo(i: 0, text: "foo") } } let object = MyClass() object.foo.i.observeNext { [weak self] value in // the weak reference is really only needed if you reference self, but if you do, make sure to make it weak to avoid strong reference cycle print("new value", value) }Y luego, cuando actualice la propiedad, por ejemplo, como se muestra a continuación, se llamará al cierre del controlador del observador:
object.foo.i.value = 42Vale la pena señalar que los marcos como Bond o RxSwift ofrecen este tipo de funcionalidad y mucho más.
Con variables puedes usar dos observadores por defecto
willSet : representa el momento antes de que la variable se establezca con un nuevo valor
didSet : representa el momento en que se estableció la variable
También en observador puedes trabajar con dos valores. Con variable actual en estado actual, y con constante dependiendo del observador
struct Struct { var variable: String { willSet { variable // before set newValue // after set, immutable } didSet { oldValue // before set, immutable variable // after set } } }Y lo mismo puede hacer con cualquier otra propiedad almacenada, por lo que también puede usarla para la variable de estructura en su clase
class Class { var myStruct: Struct? { didSet { ... } } }También puede, por ejemplo, establecer observador de notificación de publicación variable con cierto nombre
didSet { NotificationCenter.default.post(name: Notification.Name("VariableSet"), object: nil) }y luego puede agregar cierta clase como observador para recibir notificaciones con este nombre
class Class { init() { NotificationCenter.default.addObserver(self, selector: #selector(variableSet), name: Notification.Name("VariableSet"), object: nil) } deinit { NotificationCenter.default.removeObserver(self, name: Notification.Name("VariableSet"), object: nil) } @objc func variableSet() { ... } }Pruebe esto, primero cree una estructura con una variable de acción y cuando cree un objeto de la estructura, establezca el parámetro de acción en la acción que desea. ex.
struct testStruct { var action: (()->())? var variable: String? { didSet { self.action?() } }}
Y dentro de tu código principal - clase principal
var testS = testStruct() testS.action = { print("Hello") } testS.variable = "Hi"Cuando configura testS.variabe = "Hola", llamará a la impresión ("Hola")