How to Convert Legacy async code to use Combine using Swift and iOS

πŸ’‘
In the previous post, we learned how to convert the existing legacy async structure to the async-await mechanism. If you haven't read that yet, I strongly recommend going through it.

In this post, we will see how to convert legacy async code to use the Combine for performing asynchronous operations on iOS using Swift.

Looking at the legacy async code

Before we apply the modernization, let's take a look at existing legacy async code that performs the operation and loads the data in async format.


// Implementation
private func asyncProcessing(completion: (Bool) -> Void) {
    let isError = Bool.random()
    completion(isError)
}

// Execution
asyncProcessing { success in

}

Converting to use the Combine framework

In order to convert legacy code using the Combine framework, we will use the Future API that returns Promise in its closure and we can either return a successful value or the error while asynchronously executing the promise closure.


import Combine

....
..

var cancellables: Set<AnyCancellable> = []

....
..

// Async function execution

private func asyncProcessing() -> AnyPublisher<Bool, CustomError> {
    let isError = Bool.random()

    return Future { promise in
        if isError {
            promise(.failure(CustomError.genericError))
        } else {
            promise(.success(true))
        }
    }.eraseToAnyPublisher()
}

....
..

// Calling the async function
asyncProcessing().sink { completion in
    switch completion {
    case .finished:
        print("Finished")
    case .failure(let error):
        print(error.localizedDescription)
    }
} receiveValue: { success in
    print("Is Success? \(success)")
}.store(in: &cancellables)

...
..

As you can see in the above example, we have imported the Combine framework on the top. We are also using the Set of AnyCancellable objects to store all the publishers so that they don't go out of scope.

We refactored our legacy async function so that we are simply returning the Future from it. As soon as the async function completes loading the remote value, we will finish the execution by passing either a successful value or the failure in its closure.

On the next line, we are now calling the async function using sink API on Combine to receive value, catch thrown errors, and mark the completion.

Summary

And this is all. We successfully converted the legacy async code to use the modern Future and Promises-based Combine APIs. It is still possible to write an even more customizable solution by using generics so that it can be applied to any legacy async block of code in your codebase. But I will leave it for another time.

Support and Feedback

If you have any comments or questions, please feel free to reach out to me on LinkedIn.

If you like my blog content and wish to keep me going, please consider donating on Buy Me a Coffee or Patreon. Help, in any form or amount, is highly appreciated and it's a big motivation to keep me writing more articles like this.