Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RxSwift Shared Subscription Execution Order

How can I ensure that a subscriber to an Observable will receive the onNext event after another subscriber?

My example is the following:

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)

I would like for that last combineLatest binding to trigger after any of the 4 subscriptions above it have already executed. This is because it is only after the properties on accountDetails have been set that the validForSignUp will be accurate.

I am aware that a solution to this problem would be to make validForSignUp a Variable and observe it, but let's assume that is not possible.

like image 613
Infinity James Avatar asked Oct 18 '22 11:10

Infinity James


1 Answers

I would do something more like this:

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)
    }
}
like image 112
Daniel T. Avatar answered Oct 21 '22 06:10

Daniel T.