Swift Concurrency Adoption at Halodoc
At Halodoc, we are constantly trying to adopt the latest feature from Swift
releases, to improve performance, simplified code and better readability. Async await
is part of the new structured concurrency changes that arrived in Swift 5.5. This is a very simplified description, but it should give you an idea already how important concurrency in Swift
is for the performance of iOS apps. With the new async methods and await statements, we can define methods performing work asynchronously.
Asynchronous programming
is an essential part of modern app development. Promises and futures are powerful abstractions for managing asynchronous operations in Swift. One popular way to implement asynchronous programming in iOS is through PromiseKit
, a third-party library that provides a convenient way to work with Promises. Promises are a way of handling asynchronous operations by providing a placeholder object that will eventually contain the result of the operation.
However, with the introduction of Swift Concurrency in Swift 5.5, developers now have a new, built-in way to manage concurrency that helps to remove the third-party dependency. In this blog post, we'll explore how we achieved the PromiseKit behaviour using Swift concurrency.
Benefits of Swift Concurrency over PromiseKit
- Improved performance: Swift Concurrency is built into the Swift language and runtime, which means that it can be more efficient than third-party libraries like
PromiseKit
. By using Swift Concurrency, we can avoid the overhead of using an external library and improve the performance of our app. - Simplified code: With Swift Concurrency, we can write asynchronous code that looks more like synchronous code, making it easier to read and understand.
- Compatibility: It is native with Swift 5.5, which means that it is always available and up-to-date. This can reduce the risk of compatibility issues and ensure that our code is always using the latest and greatest features of Swift.
- Better error handling: It makes error handling more explicit and easier.
- Reduce app size: App size significantly reduce by removing the third party dependencies.
What is Swift Concurrency?
Swift Concurrency is a framework for writing concurrent and asynchronous code in Swift that provides a set of new language features and tools. It aims to make it easier for developers to write efficient and scalable code that takes advantage of multiple processors and cores, solving the challenges associated with concurrent programming. To use Swift Concurrency, developers need to be using Xcode 13 or later and Swift 5.5 or later, and they can take advantage of new language features such as async/await syntax and actors. The Swift Package Manager also includes support for concurrency, making it easy to incorporate third-party libraries and tools that take advantage of these new features.
It introduces two new keywords: async
and await
. async
is used to define asynchronous functions, while await
is used to wait for the result of an asynchronous function.
“Await is awaiting a callback from his buddy async”
How to transition from PromiseKit
to Swift Concurrency
To replace PromiseKit
with native Swift async/await syntax the first step in transitioning from PromiseKit
to Swift Concurrency is to replace PromiseKit
with the new async/await
syntax. This involves replacing PromiseKit's Promise object with the native Swift Task
object.
Here's how we use PromiseKit
to fetch data from a remote API:

To do the same thing with Swift Concurrency, we can use the async/await
syntax:

As we can see, the code is much simpler and easier to read.
Use Swift Concurrency's provides set of APIs
that we can use to implement asynchronous tasks. These include async let
, which allows us run multiple tasks concurrently, and TaskGroup
, which allows us group and manage a set of tasks.
Here's how we use async let
to fetch data from multiple endpoints concurrently:

Where we will be using URLSession for fetch the data as

This function returns a promise that will eventually contain a User
object. It accomplishes this by creating a new promise using Promise { seal in }
, and then using URLSession
to perform an asynchronous HTTP
request. When the request completes, the promise is either fulfilled with the User
object, or rejected with an error.
Swift Concurrency makes it easier to write concurrent code. async
is used to define asynchronous functions, while await
is used to wait for the result of an asynchronous function. Here's an example:

- This function does the same thing as the
fetchUserData()
function we defined earlier, but it uses Swift Concurrency instead ofPromiseKit
. - The
async
keyword indicates that this function is asynchronous, and theawait
keyword is used to wait for the result of thedata(from:)
method call. - This function is much simpler than the
PromiseKit
version, and it uses the built-inURLSession
API instead of thePromiseKit
wrapper. Because it usesasync
andawait
, we don't need to create a new promise, and we don't need to use thePromise
class at all.
Grouping of Async and Sync Operation
- To get user and bank list we are grouping two independent async function in
when(fullfilled:)
. and in order to get wallet detail, saved cards, and order list we have dependency on user, so we have group them use.then
and.done
.


We are calling it from ViewController
.

This is the output of above implementation -

Below code is implementation for same using swift concurrency
.
- here we are using
async
keyword to declare thatgetUser()
,getBankList()
are independent asynchronous functions, and only thangetWallet(user:)
is getting called as it is waiting for response. - we are using using
await
keyword we are making them all dependent iegetWallet(user:)
,getSavedCardList()
getOrderHistory(for:)
.


In addition to being simpler, this version of the function is also more efficient. When we use PromiseKit
, we create a new promise object for each asynchronous task we perform. This can add overhead, and it can make it harder to reason about our code. With Swift Concurrency, we can use a single function that performs the asynchronous task.
Challenges while adopting Swift Concurrency
While replacing PromiseKit
with Swift Concurrency offers many benefits, there are also some challenges we face. Here are a few of them:
- Compatibility: Swift Concurrency is only available in Swift 5.5 and later versions, so if we're using an older version of Swift, we won't be able to use it.
- Debugging: Debugging concurrency issues can be challenging, especially if we're not used to working with
async/await
syntax. we may need to spend more time debugging our code to ensure that it's functioning as expected. - Error handling: Error handling in Swift Concurrency is different from
PromiseKit
. WithPromiseKit
, we can use acatch
block to handle errors, while with Swift Concurrency, we usetry
/catch
blocks. This means that we'll need to update our error handling code. For example Let's say we have an asynchronous function that loads data from a remote API using PromiseKit, and we want to handle any errors that might occur during the loading process:

- In this example, we're using PromiseKit's
catch
block to handle any errors that might occur during the loading process. - If the API call fails or the status code is invalid, we throw a custom
DataLoadingError
error and catch it in thecatch
block.
Here's an implementation of the same function using Swift Concurrency:

- In this example, we're using a
try/catch
block to handle any errors that might occur during the loading process. - If the API call fails or the status code is invalid, we throw a custom
DataLoadingError
error and catch it in thecatch
block. - With PromiseKit, we use a
catch
block to handle errors, while with Swift Concurrency, we use atry/catch
block.
Conclusion
In conclusion, Swift Concurrency offers a native way to handle asynchronous programming in Swift. While PromiseKit
is a popular library in the Swift community, it's becoming less relevant as developers transition to using Swift Concurrency. Replacing PromiseKit
with Swift Concurrency requires learning a new set of APIs and syntax, but the benefits of Swift Concurrency, such as the simplification of async code, make the transition worthwhile. By leveraging Swift Concurrency's Task-based concurrency model and async/await
syntax, we can manage async code more effectively and with greater readability just like we did at Halodoc.
Join us
Scalability, reliability, and maintainability are the three pillars that govern what we build at Halodoc Tech. We are actively looking for engineers at all levels and if solving hard problems with challenging requirements is your forte, please reach out to us with your resumé at careers.india@halodoc.com.
About Halodoc
Halodoc is the number 1 all around Healthcare application in Indonesia. Our mission is to simplify and bring quality healthcare across Indonesia, from Sabang to Merauke. We connect 20,000+ doctors with patients in need through our Tele-consultation service. We partner with 3500+ pharmacies in 100+ cities to bring medicine to your doorstep. We've also partnered with Indonesia's largest lab provider to provide lab home services, and to top it off we have recently launched a premium appointment service that partners with 500+ hospitals that allow patients to book a doctor appointment inside our application. We are extremely fortunate to be trusted by our investors, such as the Bill & Melinda Gates Foundation, Singtel, UOB Ventures, Allianz, GoJek, Astra, Temasek and many more. We recently closed our Series C round and In total have raised around USD$180 million for our mission. Our team works tirelessly to make sure that we create the best healthcare solution personalised for all of our patient's needs, and are continuously on a path to simplify healthcare for Indonesia.