RxSwift共享订阅执行顺序

时间:2017-05-26 14:53:57

标签: swift rx-swift reactive

如何确保Observable的订阅者在另一个订阅者之后收到onNext个活动?

我的例子如下:

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)

我希望最后一个combineLatest绑定在它上面的任何4 subscriptions已经执行之后触发。这是因为只有在设置accountDetails上的属性后,validForSignUp才会准确。

我知道解决这个问题的方法是让validForSignUp成为Variable并观察它,但我们假设这是不可能的。

2 个答案:

答案 0 :(得分:2)

我会做更像这样的事情:

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)
    }
}

答案 1 :(得分:1)

您可以通过一次订阅处理数据验证。以下是我将如何将组合事件绑定到执行组合结果验证的Observable的方法。

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

将提供合并结果验证的Observable可能类似于以下函数。

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

        /* Is the data valid? */

        return result;
    }
}

每当有来自任何字段Observable的新数据时,Observable validFields将使用验证结果进行更新。使用combineLatest,您可以获得维护源Observable的顺序的额外好处。该解决方案是在不依赖于存储状态使其完全反应的情况下完成的。