En el imperativo Swift, es común usar propiedades calculadas para proporcionar un acceso conveniente a los datos sin duplicar el estado.
Digamos que tengo esta clase hecha para uso MVC imperativo:
class ImperativeUserManager {
private(set) var currentUser: User? {
didSet {
if oldValue != currentUser {
NotificationCenter.default.post(name: NSNotification.Name("userStateDidChange"), object: nil)
// Observers that receive this notification might then check either currentUser or userIsLoggedIn for the latest state
}
}
}
var userIsLoggedIn: Bool {
currentUser != nil
}
// ...
}
Si quiero crear un equivalente reactivo con Combine, por ejemplo, para usar con SwiftUI, puedo agregar fácilmente @Published
a las propiedades almacenadas para generar Publisher
s, pero no para las propiedades calculadas.
@Published var userIsLoggedIn: Bool { // Error: Property wrapper cannot be applied to a computed property
currentUser != nil
}
Hay varias soluciones que se me ocurren. Podría hacer que mi propiedad calculada se almacene en su lugar y mantenerla actualizada.
Opción 1: Usar un observador de propiedades:
class ReactiveUserManager1: ObservableObject {
@Published private(set) var currentUser: User? {
didSet {
userIsLoggedIn = currentUser != nil
}
}
@Published private(set) var userIsLoggedIn: Bool = false
// ...
}
Opción 2: Usar a Subscriber
en mi propia clase:
class ReactiveUserManager2: ObservableObject {
@Published private(set) var currentUser: User?
@Published private(set) var userIsLoggedIn: Bool = false
private var subscribers = Set<AnyCancellable>()
init() {
$currentUser
.map { $0 != nil }
.assign(to: \.userIsLoggedIn, on: self)
.store(in: &subscribers)
}
// ...
}
Sin embargo, estas soluciones alternativas no son tan elegantes como las propiedades calculadas. Duplican el estado y no actualizan ambas propiedades simultáneamente.
¿Cuál sería un equivalente apropiado para agregar Publisher
a una propiedad calculada en Combine?
ObservableObject
. Inherentemente asume que unObservableObject
objeto debería tener una capacidad de mutación que, por definición, no es el caso de la propiedad calculada .Respuestas:
¿Qué tal el uso aguas abajo?
De esta manera, la suscripción obtendrá un elemento de upstream, luego puede usar
sink
oassign
hacer ladidSet
idea.fuente
Cree un nuevo editor suscrito a la propiedad que desea rastrear.
Entonces podrá observarlo como si fuera su
@Published
propiedad.No está directamente relacionado pero es útil, sin embargo, puede rastrear múltiples propiedades de esa manera
combineLatest
.fuente
Debes declarar un PassthroughSubject en tu ObservableObject:
Y en el didSet (willSet podría ser mejor) de su var @Published usará un método llamado send ()
Puede verificarlo en la charla de flujo de datos WWDC
fuente
@Published
wrapper yPassthroughSubject
ambos sirven el mismo propósito en este contexto. Presta atención a lo que escribiste y a lo que el OP realmente quería lograr. ¿Su solución sirve como la mejor alternativa que la opción 1 en realidad?scan ( : :) Transforma elementos del publicador ascendente al proporcionar el elemento actual a un cierre junto con el último valor devuelto por el cierre.
Puede usar scan () para obtener el valor más reciente y actual. Ejemplo:
El código anterior es equivalente a esto: (menos Combinar)
fuente