简体   繁体   中英

Bind RxSwift observable result to another observables

I have an observable that manages an object called Profile:

class Profile {
    let name. : String
    let surname : String
    let avatar: String?
}
Observable<Profile>

This observable is binded to view model, who has a at the same time an observable property called

var initialMarks:Observable<String>

That are the initial letters of name and surname properties. This initial letters are shown in case avatar property is nil.

I have a function that returns initial letters in a string:

func initialLetters(first:String, second: String) -> String

How can I achieve this by using RxSwift any operators, to:

  1. Access avatar property.
  2. Check if it is nil.
  3. if it is nil, call function initialLetters.
  4. Bind function initialLetters to observable initialLetters.

Many thanks.

This is just a simple mapping...

Here it is as it's own view model.

func initialMarks(for profile: Observable<Profile>) -> Observable<String> {
    return profile
        .compactMap { profile in
            if profile.avatar == nil {
                return initialLetters(first: profile.name, second: profile.surname)
            }
            else {
                return nil
            }
        }
}

If you want to embed it in a view model struct/class you could do something like:

struct ViewModel {
    let profile: Observable<Profile>

    var initialMarks: Observable<String> {
        return profile
            .map { profile in
                if profile.avatar == nil {
                    return initialLetters(first: profile.name, second: profile.surname)
                }
                else {
                    return ""
                }
            }
    }
}

Also the two implementations above are subtly different depending on what you want... In the first one, it only emits initials when the avatar is nil, the second one emits an empty string when the avatar exists or initials when the avatar in nil.

For this particular example you need to use a PublishSubject , so you can bind your stream initialMarksSubject and observe to it as Observable

Setup

private var initialMarksSubject = PublishSubject<String>()
private let disposeBag = DisposeBag()

private func observeInitialMarks() {
    profileObservable
        .filter { $0.avatar == nil } // only take elements that have a nil avatar
        .map { self.initialLetters(first: $0.name, second: $0.surname) } // transform profile to initialLetters of type String
        .bind(to: initialMarksSubject) // bind the stream to your subject
        .disposed(by: disposeBag)
}

var initialMarks: Observable<String> {
    return initialMarksSubject.asObservable()
}

Explanation

Calling observeInitialMarks() would:

  1. Subscribe to profileObservable
  2. Accept only profiles that have no avatar
  3. Transform the profile to initialLetters
  4. Bind the events to initialMarksSubject

Subscribing to initialMarks would return the information that you need to display for your UI

Usage to your UI Component

viewModel.initialMarks
    .observeOn(MainScheduler.instance)
    .bind(to: profileLabel.rx.text)
    .disposed(by: disposeBag)

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