Purpose of the article: iOS development
Intended Audience: iOS Developers
Tools and Technology: iOS, Swift 5.5, XCode
Keywords: Swift, iOS Development, XCode, async/await, new in Swift, swift5.5
This article is about the async/await concept, which is new in Swift 5.5. At WWDC2021, Apple introduced this concept. With this async/await, we are going to write asynchronous code.
Generally, when we call the synchronous function, it will block our thread. But when we call the asynchronous function-like function with a completion handler, our thread will be free to do other work. When it’s done, it will notify us by calling its completion handler. In iOS, there are many asynchronous functions available.
Let’s discuss the following example.
We have a list of row items. Each row will display a thumbnail image, which is stored on the server. We have a fetchThumbnail method in our view model to fetch and display thumbnail images in a list.
In this example, it will transform a string to a UIImage via a series of steps:
- thumbnailURLRequest — This method will create a URLRequest from the string
- dataTask — URLSession’s dataTask method will get the data for that request
- UIImage in it with data — This will create an image from that data
- UIImage’s prepare thumbnail — This method will render a thumbnail from the original image
Each of the above operations depends on the previous operation’s results. That means we need to perform the operations in sequence.
Some of these operations return values quickly, like the first and third steps. But the other two operations will take time to finish. That’s why we have asynchronous functions to complete those tasks.
As we see in the example, we are passing two parameters one is a string needed for the first operation, and the second is a completion handler function used to return the result callback. When the operation succeeds, we will send the thumbnail image to the completion handler. Else we need to send the appropriate error. Hence, we are doing multiple operations-we need to call the completion handler in five places. In some cases, we may forget to call the completion handler in places. And finally, we end up with 20 lines of code for this method.
Now let us rewrite this function with async/await. See the example below.
Now, the fetchThumbnail method has only one parameter and no completion handler function. Instead, we need to add the “async throws “keyword to the function. Here, in the second step, we are calling the asynchronous data function from the foundation. This method is awaitable.
We added “Try” before the data function because that function is marked “throws” likewise.
When we need to call the “async” function, “await” is needed.
If the expression has multiple async function calls, we need to write “await” only once.
When the data finishes downloading, it will resume and return to fetchThumbnail. If there is no error, the data and response variables will be defined. Otherwise, it will throw an error. This is like the previous version of fetchThumbnail. But the awaitable version is much simpler. So, we need to make a request and assign the values to variables to use them.
In the next step, we have created a UIImage from the downloaded data. If it succeeds, then a thumbnail will be rendered by accessing the thumbnail property for the image. When the thumbnail is formed, the thread is free to do other things. Once the thumbnail is formed, it resumes and returns to fetchThumbnail. If the thumbnail is rendered successfully, it will return the thumbnail. Otherwise, it will throw an error.
That’s it; we are done. Instead of 20 lines of code, now it’s six lines only. And it’s all straight-line code. In the last step, rendering the thumbnail is marked with “await.” The thumbnail property is async. Not only the functions can be async. Properties can also be async. In the following example, the thumbnail property is not part of the SDK. It’s a property written in an extension to UIImage.
In the above thumbnail property, we form CGSize and pass it to the byPreparingThumbnail(ofSize). This is the awaitable version of the method in UIImage.
Here, we can notice a couple of things. One is that it has an explicit getter. This is required for marking a property async. In Swift 5.5, property getters can also throw. Same as async function signatures, if a property is both async and throws, the async keyword goes right before the throws keyword. Another one is that the property has no setter. Only read-only properties can be async.
There is another place where await can be used for loops to iterate over async sequences. For more detail about the async sequence, check the below link.
- When we mark a function async, we are allowing it to suspend. When a function suspends itself, it will suspend its callers also
- An async function may suspend one or more times the await keyword is used
- When an async function is suspended, the thread is not blocked. So, the system will be free to do other tasks
Finally, when an async function resumes and returns the results to the original function, and execution continues right where it left off.
Bridging from sync to async
For example, if we need to change the code from sync to async, we have a list of posts, and each post has an ID. We need to pass that ID to ViewModel to get a thumbnail.
First, we need to remove the completion handler and add ‘try’ for handling errors, and to complete the call to an async function, add await.
When you try to build the code, the compiler will show an error that you can’t call async functions in contexts that are not async. In the above example, the onAppear modifier takes a plain non-async closure. We need to bridge this gap between synchronous and asynchronous.
The solution to this is to use the async task function. An async task will take up the work in the closure, and on the next available thread, it will be sent to the system for immediate execution. The main benefit here is that async code can be called from inside of sync contexts. Now, if we rebuild, the compiler will satisfy.
That is all for now. You can check out more details at apple WWDC2021.