Clashes amid India and US on Oil issues

The US has questioned all republics, as well as India, to end all oil imports from Iran from November as it ruled out any exception to India and Indian businesses from its Reimposed Iranian…

Smartphone

独家优惠奖金 100% 高达 1 BTC + 180 免费旋转




Using a TaskGroup for parallel network requests in Swift

Image by author

This article is for engineers, who are already familiar with async/await principles and know how SwiftUI Views work, cause there are some unexplained basic declarations related to those topics. Therefore, if you don’t know how they operate, please inspect this subject and then be sure to come back, cause TaskGroups are super-powerful and you indeed have to be acquainted with them.

Async/await mechanism, presented by Apple at WWDC 2021, is very fast, performant and modern way of conducting asynchronous tasks with Swift. One of the abilities iOS developers were given with is making parallel asynchronous tasks with help of a TaskGroup:

There a few types of task groups, but in this particular article we are gonna be working with throwing variant of its implementation, which is actually a task group, that can potentially throw an error:

If you draw an analogy between regular and throwing function, it might help you to visualise the difference. So, with that said, let’s get coding!

Imagine you are working for a company of American animated science fiction sitcom fans and they asked you to create an app which will reflect information about all Rick and Morty universe characters.

For the sake of simplicity, we are using a shortened data model throughout this article (but if you play with the API, there is much more about each character):

In order to conduct a network call, you will need a URLRequest instance. Again, to simplify everything, we are having this extension, which accepts the character id and interpolates it into a prepared-in-advance URL:

We are using a super-simple SwiftUI view, that just has only one Text component, into which the rickAndMortyService.characters string is added through a ‘publishing’ way. This string is being updated inside of RickAndMortyService after loadCharacters(...) function is called, API responses are received and then transformed into the resulting string.

By now we are ignoring catch blocks, but know that reauthorization process can be carried out pretty easily using a recursion.

RickAndMortyService is responsible for building an array of URLRequests basing on passed ids (line 14), calling theLoader class with those requests (line 16, class will be shown further), and, after Loader returns an array of RickAndMortyResponse, formatting this array into a resulting string:

Let’s now observe the Loader class, which is actually responsible for conducting network requests:

Looks complicated right? 😅 I understand, let’s break it into blocks and work each of them out:

Firstly, let’s mention the article culprit. The whole code of conduct(…) function is wrapped into the withThrowingTaskGroup(of:returning:body:) function:

Input parameters are:

As we know that the group is returning a [Decodable], we can return it right away in the conduct(…) function, return parameter of which is [Decodable] as well.

Secondly, let’s see how group tasks are being produced:

In simple words, we are populating the group by tasks to load the data for each URLRequest, which is passed into the conduct(…) function. So, obviously, amount of tasks is going to be equal to the amount of requests. Each task is responsible for firing URLSession.shared.data(…), getting Data from server and returning the dictionary, where the key is the url, which has been used for request, and the value is a Data, returned from this request. It will be needed for ordering asynchronous responses further, cause you never know in which disposition you will receive them, but it is nice to have responses in the same order as requests were done.

Important note: tasks are not fired when added, they do their job only when you try to get the value from the group (as described on the next step)

Thirdly, let’s see actually how true [String: Data] values are received from that complicated ThrowingTaskGroup<[String : Data], Error>:

First of all we check if all requests have urls, this little trick allows us to safely force-unwrap url values further.

Then we create an enumerated orderedUrls array to keep track of executed requests order. This way we state that request with first url will have the offset 0, with second url offset 1 and so on.

That is the moment when tasks are launched. TaskGroup SDK is populated with a number of higher order functions, which allow you to get a needed value from the task group. You can check them out on the Apple website with the link I’ve provided at the beginning.

What reduce(into:…) does is simply reducing the group value into a provided collection. Trailing closure parameters are:

We have [Int: Data] collection in order to associate the executed request order with a response to that request, so we have a correct sequence of responses to return further.

Fourthly (and finally), we can get our actual responses, which are casted into the passed type as an input conduct(…) function parameter:

As we have every response associated with a number of request, that has been executed, we can sort those responses by their number and then, with help of first map get only values (aka only responses), and with help of second map get actual data models, casted respectively to a passed type.

Actually this last part is going to be returning the TaskGroup return type, that has been declared at the very beginning of its declaration (generic [Response] which is basically an array of Decodable types).

Wow, that wasn’t an easy walk 😰

But to make it worth, let’s look at our SwiftUI preview:

PreviewProvider for RickAndMortyView

Remember the order of identifiers we utilised to conduct parallel requests (5, 3, 11, 4 , 9)? You can see that responses were ordered in the same way as requests were done. Cool yeah?

Async/await in general and TaskGroup in particular are super-performant and rapid mechanisms, that in 2023 should be used instead of GCD and completion handlers wherever possible, cause they will make your app modern and will noticeably simplify creating of asynchronous tasks and running them within your code base.

Add a comment

Related posts:

How to track Google Ads Conversions in Google Analytics 4?

Set up a GA4 property: Create a new GA4 property in your Google Analytics account if you haven’t already done so. Ensure that you have the necessary permissions to create and manage properties. Link…

La noblesse du nihilisme

Le nihiliste est un homme de paille utilisé par les conservateurs pour empêcher le doute

Facility Billing

Facility Billing is the technical charge for the hospital’s services that are provided in an outpatient department of a hospital. Facility costs are the opposite of physician-based billing as its…