简体   繁体   中英

How do I emulate a computed property with access to the latest value in RxSwift?

If I want to emulate a standard property of eg a Bool in RxSwift I can use let isValid = Variable<Bool>(false) and then use .value to get the last value inline and .asObservable() to access the stream.

However I want to emulate a computed propery eg var isValid { return self.password.characters.count > 0 } and also be able to get the last value inline as well as in the form of an observable stream.

I want to be able to do both so I can write code like ...

if isValid.value { // isValid is Variable<Bool>
    // ... do something ....
}

as well as bind to eg a TextField

I know I can write as a pure Observable as follows ...

var isValid: Observable<Bool> {
    return self.username.asObservable().map { username in   // username is Variable<String>
        return username.characters.count > 0
    }
}

but then I have to refactor the previous example to be ....

isValid.subscribe { isValid in 
    if isValid.element {
        // ... do something ....
    }
}.dispose(of: self.disposeBag)

How then do I express a computed property in RxSwift which can be consumed as an inline value as well as a stream?

I had the same problem and ended up with a solution that does not look clean but works. I would add a new var valid: Bool = false and a subscription to the isValid observable that updates it

isValid.subscribe(onNext:{ [weak self] value in self?.valid = valid})

With this isValid is used for binding and subscriptions and valid for use in imperative code.

Btw your definition of isValid is kind of "unnatural", just make it a let isValid: Observable<Bool> and assign it in init like isValid = username.asObservable().map({$0.characters.count > 0})

Update:

Another solution is using RxBlockling, you can get the current value with try! isValid.toBlocking().single() try! isValid.toBlocking().single() but I think it is a worse solution and generally not recommended.

Try this:

let username = Variable("")

private let _isValid: Observable<Bool> = username.asObservable().map{ $0.characters.count > 0 }

let isValid = Variable(false)

Then, in your init , viewDidLoad or wherever:

_isValid.asObservable()
    .bind(to: isValid)
    .disposed(by: bag)

You can test it with:

isValid.asObservable()
    .subscribe(onNext: {
        print("value: \($0)")

    })
    .disposed(by: disposeBag)

username.value = "xfreire"

// OUTPUTS:
// value: false
// value: true

and you are still able to do:

if isValid.value {
    // ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM