Search code examples
iosswiftrx-swift

How to chain RxSwift Singles in order


Let's say there are 3 methods I want to run in order and entry point is dependent on an integer(number).

func method1() -> Single<Void> {
    return .just(())
}

func method2() -> Single<Void> {
    return .just(())
}

func method3() -> Single<Void> {
    return .just(())
}

in regular Swift there would be switch like this:

func doThings(startingFrom number: Int) -> Single<Void> {
    switch number {
    case 0:
        method1()
        fallthrough
    case 1:
        method2()
        fallthrough
    case 2:
        method3()
        fallthrough
    default:
        print("Finished")
    }

    return .just(())
}

but considering method1(), method2() and method3 are Single I am thinking there should be a way in RxSwift to chain them without messing with their execution order.

I thought something like this but this works synchronous therefore there is no guaranteed execution order for methods.

public func doThings(startingFrom number: Int) -> Single<Void> {
    var actions: [Single<Void>] = []
    switch number {
    case 0:
        actions.append(method1())
        fallthrough
    case 1:
        actions.append(method2())
        fallthrough
    case 2:
        actions.append(method3())
        fallthrough
    default:
        break
    }

    return Single
        .zip(actions)
        .do(onSuccess: { _ in
            Log.debug("Finished")
        })
        .mapToVoid()
}

Sorry if I don't make sense, I'm quite new to RxSwift. Please correct me if something is wrong.


Solution

  • Your solution was very close. Just use concat instead of zip and you got it.

    My bad, Single is missing the concat operator so you have to convert to Observable or Completable first. Here are a couple of options:

    func doThings(startingFrom number: Int) -> Single<Void> {
        Observable.concat(
            [method1(), method2(), method3()]
                .dropFirst(number)
                .map { $0.asObservable() }
        )
        .toArray()
        .map(to: ())
        .do(onSuccess: { _ in
            print("Finished")
        })
    }
    
    func doThingsʹ(startingFrom number: Int) -> Single<Void> {
        Completable.concat(
            [method1(), method2(), method3()]
                .dropFirst(number)
                .map { $0.asCompletable() }
        )
        .andThen(.just(()))
        .do(onSuccess: { _ in
            print("Finished")
        })
    }