1

I have two functions with the following signatures:

import Combine

func firstCall() -> Future<Result<Void, Error>, Never> {
    return Future { promise in
        promise(.success(.success(())))
    }
}



func secondCall() -> Future<Result<Void, Error>, Never> {
    return Future { promise in
        promise(.success(.success(())))
    }
}


// Execute 1st call, then if it succeeds, execute the 2nd call

How can I execute the 2nd function only after the 1st one completes successfully?

Ideally, I'm looking to something like this:

executeInSequence([firstCall(), secondCall()])
.onResult( { finalResult in
print(finalResult)
})

Feel free to slightly adjust API signatures if that helps the call site.

More information: one of the calls is actually a converted synchronous method call with a signature of:

func secondSynchronousCall() throws {
}

    private func secondCall() -> Future<Result<Void, Error>, Never> {
        return Future { [weak self] promise in
            do {
                try self?.secondSynchronousCall()
                promise(.success(.success(())))
            } catch {
                promise(.success(.failure(error)))
            }
        }
    }
Richard Topchii
  • 7,075
  • 8
  • 48
  • 115
  • Can you use `Future` instead? It will make life much easier. – Sweeper Sep 28 '21 at 06:50
  • Following the advice from here: https://stackoverflow.com/questions/69311371/combine-sink-ignore-receivevalue-only-completion-is-needed Feel free to re-engineer the API to use `Future` instead. I'm more interested in the general idea rather than specific function signatures. I've added few details to clarify the general idea and where I'm coming from. – Richard Topchii Sep 28 '21 at 06:53
  • 1
    Your question is basically [this](https://stackoverflow.com/questions/60582659/in-combine-how-do-i-run-a-series-of-futures-in-sequence), but with `Future, Never>`. By using that horrendous type, you need to write your own specialised `append` operator that does not append if the result is `failure`. My advice, stop using `Future, Never>`, and use LuLuGaGa's answer from your previous post (but with a `Future` extension instead of a `CurrentValueSubject`). This way you can use the built in `append`. – Sweeper Sep 28 '21 at 07:23
  • Yeah, good idea. The horrendous type while looks good at call site causes too many problems. – Richard Topchii Sep 28 '21 at 07:37

1 Answers1

1

In Combine, Futures are just specialised publishers. Given you deal with publishers, you would do something like this:

let cancellable = firstCall()
.tryMap { result in
    try result.get()
}
.flatMap { _ in
    secondCall()
}
.tryMap { result in
    try result.get()
}
.sink { completion in
    print(completion)
} receiveValue: { _ in
    print("reveiveValue")
}

You can write it more concisely, however, I would suggest to simplify your Output and Error types first, as already pointed out in the comments:

For example:

func firstCall2() -> Future<Void, Error> {
    return Future { promise in
        promise(.success(()))
        //promise(.failure(MyError()))
    }
}

func secondCall2() -> Future<Void, Error> {
    return Future { promise in
        promise(.success(()))
    }
}

and then:

let cancellable2 = firstCall2()
.map(secondCall2)
.sink { completion in
    print(completion)
} receiveValue: { _ in
    print("reveiveValue")
}
CouchDeveloper
  • 18,174
  • 3
  • 45
  • 67