Dans Swift impératif, il est courant d'utiliser des propriétés calculées pour fournir un accès pratique aux données sans dupliquer l'état.
Disons que j'ai cette classe conçue pour une utilisation impérative de MVC:
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 je veux créer un équivalent réactif avec Combine, par exemple pour une utilisation avec SwiftUI, je peux facilement ajouter @Published
aux propriétés stockées pour générer des Publisher
s, mais pas pour les propriétés calculées.
@Published var userIsLoggedIn: Bool { // Error: Property wrapper cannot be applied to a computed property
currentUser != nil
}
Il existe différentes solutions de contournement auxquelles je pourrais penser. Je pourrais plutôt stocker ma propriété calculée et la mettre à jour.
Option 1: Utilisation d'un observateur de propriétés:
class ReactiveUserManager1: ObservableObject {
@Published private(set) var currentUser: User? {
didSet {
userIsLoggedIn = currentUser != nil
}
}
@Published private(set) var userIsLoggedIn: Bool = false
// ...
}
Option 2: utiliser un Subscriber
dans ma propre classe:
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)
}
// ...
}
Cependant, ces solutions de contournement ne sont pas aussi élégantes que les propriétés calculées. Ils dupliquent l'état et ne mettent pas à jour les deux propriétés simultanément.
Quel serait l'équivalent approprié d'ajouter un Publisher
à une propriété calculée dans Combine?
ObservableObject
. Vous supposez intrinsèquement qu'unObservableObject
objet devrait pouvoir avoir une capacité de mutation qui, par définition, n'est pas le cas pour la propriété calculée .Réponses:
Que diriez-vous d'utiliser en aval?
De cette façon, l'abonnement obtiendra l'élément en amont, alors vous pouvez utiliser
sink
ouassign
pour faire l'didSet
idée.la source
Créez un nouvel éditeur abonné à la propriété que vous souhaitez suivre.
Vous pourrez alors l'observer un peu comme votre
@Published
propriété.Pas directement lié, mais néanmoins utile, vous pouvez suivre plusieurs propriétés de cette façon avec
combineLatest
.la source
Vous devez déclarer un PassthroughSubject dans votre ObservableObject:
Et dans le didSet (willSet pourrait être meilleur) de votre var @Published, vous utiliserez une méthode appelée send ()
Vous pouvez le vérifier dans la discussion sur le flux de données WWDC
la source
@Published
wrapper et lesPassthroughSubject
deux servent le même objectif dans ce contexte. Faites attention à ce que vous avez écrit et à ce que le PO voulait réellement réaliser. Votre solution est-elle la meilleure alternative à l' option 1 ?scan ( : :) Transforme les éléments de l'éditeur en amont en fournissant l'élément actuel à une fermeture avec la dernière valeur renvoyée par la fermeture.
Vous pouvez utiliser scan () pour obtenir la valeur la plus récente et actuelle. Exemple:
Le code ci-dessus est équivalent à ceci: (moins Combiner)
la source