Kristijan Delivuk
Kristijan Delivuk

Reputation: 1252

RxSwift Chaining two signals in right order

So basically I have two actions I need to execute:

They have to be done in right order because getting user profile cannot be done without logging in first.

So I had bunch of code that looked like this:

func signIn(signinParameters: SignInParameters) -> Observable<SignInResult> {
        return Observable<SignInResult>.create { [unowned self] observer in

            self.signinParameters = signinParameters

            self.apiConnector
                .signIn(with: signinParameters)
                .do(onNext: { [weak self] signinResult in

                    self!.apiConnector
                        .get()
                        .do(onNext: { user in

                            let realm = RealmManager.shared.newRealm()!

                            let realmUser = RealmUser()
                            realmUser.configure(with: user, in: realm)

                            try? realm.write {
                                realm.add(realmUser, update: true)
                            }

                            self!.setState(.authenticated)

                            observer.onNext(signinResult)

                        }, onError: { (error) in
                            observer.onError(error)
                        }, onCompleted: {
                            observer.onCompleted()
                        }).subscribe()

                    }, onError: { error in
                        observer.onError(error)
                }, onCompleted: {
                    print("completed")
                    observer.onCompleted()
                }).subscribe()

            return Disposables.create()
        }

I know this is not right because I cannot send onNext signal with signin result when both actions are finished. I've been reading and I figured out i need to flatmap both actions, combine them into one signal and then manipulate signinresult but I dont have a clue how to do that. So any help would be nice.

Thank you

EDIT 1:

so I've refactored code to look something like this, but there is still problem that I can't send signal when BOTH actions are finished, or am I wrong?

func signIn(signinParameters: SignInParameters) -> Observable<SignInResult> {
    return Observable<SignInResult>.create { [unowned self] observer in
        self.signinParameters = signinParameters

    self.apiConnector
        .signIn(with: signinParameters)
        .do(onNext: { (result) in

        }, onError: { (error) in

        }, onCompleted: { 

        })
        .flatMap({ (result) -> Observable<User> in
            self.apiConnector.get().asObservable()
        })
        .do(onNext: { (user) in

        }, onError: { (error) in

        }, onCompleted: { 

        }).subscribe()

        return Disposables.create()
    }
}

Upvotes: 0

Views: 1076

Answers (1)

iWheelBuy
iWheelBuy

Reputation: 5679

Your code is not very clean and it is hard to understand what is going on (my opinion).

If you need two actions to be executed you can create two functions:

struct Parameters{}
struct Profile{}
struct User{}

func login(parameters: Parameters) -> Observable<User> {
    // get user
}

func profile(user: User) -> Observable<Profile> {
    // get profile
}

func serial(parameters: Parameters) -> Observable<Profile> {
    return login(parameters: parameters).flatMap({ profile(user: $0) })
}

login function or profile function can be also split into smaller functions if required:

func profileStored(user: User) -> Observable<Profile?> {
    // get stored profile
}

func profileRequested(user: User) -> Observable<Profile> {
    // get profile from network
}

func profile(user: User) -> Observable<Profile> {
    let observable = profileStored(user: user)
        .shareReplayLatestWhileConnected()
    let observableStored = observable
        .filter({ $0 != nil })
        .map({ $0! })
        .shareReplayLatestWhileConnected()
    let observableRequested = observable
        .filter({ $0 == nil })
        .flatMap({ _ in profileRequested(user: user) })
        .shareReplayLatestWhileConnected()
    return Observable
        .of(observableStored, observableRequested)
        .merge()
        .shareReplayLatestWhileConnected()
}

As a result you can mix smaller functions with flatMap or any other operator.

That is how I do it. Hope it'll be helpful

Upvotes: 1

Related Questions