2017-05-26 3 views
3

Comment puis-je m'assurer qu'un abonné à un Observable recevra l'événement onNext après un autre abonné?Commande d'exécution d'abonnement partagé RxSwift

Mon exemple est le suivant:

let firstNameObservable = firstName.asObservable().shareReplay(1) 
let lastNameObservable = lastName.asObservable().shareReplay(1) 
let bioObservable = bio.asObservable().shareReplay(1) 
let websiteObservable = website.asObservable().shareReplay(1) 

firstNameObservable.subscribe(onNext: { [unowned self] firstName in self.accountDetails.firstName = firstName }).disposed(by: disposeBag) 
lastNameObservable.subscribe(onNext: { [unowned self] lastName in self.accountDetails.lastName = lastName }).disposed(by: disposeBag) 
bioObservable.subscribe(onNext: { [unowned self] bio in self.accountDetails.bio = bio }).disposed(by: disposeBag) 
websiteObservable.subscribe(onNext: { [unowned self] website in self.accountDetails.website = website }).disposed(by: disposeBag) 

Observable.combineLatest(firstNameObservable, lastNameObservable, bioObservable, websiteObservable) 
    { [unowned self] _, _, _, _ in return self.accountDetails.validForSignUp } 
    .bind(to: isValid) 
    .disposed(by: disposeBag) 

Je voudrais que cette dernière combineLatest soit déclenché après l'une des 4 subscriptions au-dessus ont déjà exécuté. C'est parce que c'est seulement après que les propriétés sur accountDetails ont été définies que le validForSignUp sera précis.

Je suis conscient qu'une solution à ce problème serait de faire validForSignUp un Variable et de l'observer, mais supposons que ce n'est pas possible.

+0

Est-ce que 'isValid' est' Variable'? Vous devriez éviter les variables et les sujets si possible. –

Répondre

2

je ferais quelque chose comme ceci:

let accountDetails = AccountDetails(firstName: firstName.orEmpty.asObserable(), lastName: lastName.orEmpty.asObservable(), bio: bio.orEmpty.asObservable(), website: website.orEmpty.asObservable()) 

accountDetails.validForSignup 
    .bind(to: isValid) 
    .disposed(by: bag) 

struct AccountDetails { 

    let validForSignup: Observable<Bool> 

    init(firstName: Observable<String>, lastName: Observable<String>, bio: Observable<String>, website: Observable<String>) { 

     let firstNameValid = firstName.map { $0.isValidFirstName } 
     let lastNameValid = lastName.map { $0.isValidLastName } 
     let bioValid = bio.map { $0.isValidBio } 
     let websiteValid = website.map { $0.isValidWebsite } 

     validForSignup = Observable.combineLatest([firstNameValid, lastNameValid, bioValid, websiteValid]) { $0.allTrue() } 
    } 
} 

extension String { 
    var isValidFirstName: Bool { 
     return !isEmpty // put approprate code here 
    } 

    var isValidLastName: Bool { 
     return !isEmpty // put approprate code here 
    } 

    var isValidBio: Bool { 
     return !isEmpty // put approprate code here 
    } 

    var isValidWebsite: Bool { 
     return !isEmpty // put approprate code here 
    } 

} 

extension Sequence where Iterator.Element == Bool { 

    func allTrue() -> Bool { 
     return !contains(false) 
    } 
} 
+0

Peut-être que la solution à ce problème est, en effet, une approche différente. Merci d'avoir répondu. –

+0

Oui, si vous écrivez votre code réactif de sorte que l'ordre compte de cette façon, alors vous le faites mal. –

1

Vous pouvez gérer votre validation de données avec un seul abonnement. Voici comment cela peut être fait où je vais lier les événements combinés à un observable qui effectue la validation des résultats combinés.

let fields = [firstNameObservable, 
       lastNameObservable, 
       bioObservable, 
       websiteObservable] 
let validFields = Observable.combineLatest(fields).bind(to: validateFields) 

L'observable qui va livrer la validation des résultats combinés pourrait ressembler à la fonction suivante.

func validateFields<T>(allFields: Observable<[T]>) -> Observable<Bool> 
{ 
    return allFields.map { fieldData in 
     var result: Bool = false 

     /* Is the data valid? */ 

     return result; 
    } 
} 

Le Observable validFields sera mis à jour avec un résultat de validation chaque fois qu'il ya de nouvelles données de l'une des Observables sur le terrain. Avec combineLatest, vous avez l'avantage supplémentaire que l'ordre de vos Observables source est maintenu. Cette solution est accomplie sans recourir au stockage d'état le rendant complètement réactif.

+0

Mon but était en fait d'assurer l'exécution de la validation suite au paramétrage des propriétés 'AccountDetails'. Bien que votre réponse soit intéressante, elle ne me donne pas forcément une solution à mon problème spécifique. –