Async await in Swift explained

Reading Time: 5 minutes

Async await?

Async await is part of the new structured concurrency changes which were launched in Swift 5.5 during WWDC 2021. As we already know Concurrency in Swift means allowing multiple pieces of code to run at the same time, and is very important for the performance of our apps. With the new Async await, we can define methods performing work asynchronously.

Now that “Async await” is finally here, we can simplify our code with async methods and await statements and make our asynchronous code easier to read.

Async is?

Async is asynchronous and can be seen as a method attribute, which method performs asynchronous work. As an example we can use the method below:

func getProducts() async throws -> [Products] {
    // ... perform here data request

The getProducts method is defined as async throwing, which means that it’s performing a fail-able asynchronous job. The method would return an array of custom objects Product if everything went well or would throw an error if something went wrong.

How async can replace completion callbacks

Async methods replace the often seen completion callbacks. Completion callbacks (closure) were common in Swift to return from an asynchronous task, often combined with a Result type parameter. The above method would have been written as followed:

func getProducts(completion: (Result<[Product], Error>) -> Void) {
    // ... perform here data request

Creating a method with a completion closure is still possible in Swift, but it has a few disadvantages which are solved by using async instead:

  • Developer has to make sure to call the completion closure in each possible method exit, if not doing so will possibly result in an app waiting for a result infinitely.
  • Callbacks (closures) are harder to read. It’s not as easy to read about the order of execution as compared to how easy it is with structured concurrency.
  • Retain cycles has to be avoided using weak references.
  • Implementers has to switch over the result to get the outcome, also it’s not possible to use try catch statements.

These disadvantages are based on the closure version using the relatively new Result enum. It’s likely that a lot of projects still make use of completion callbacks without this enumeration:

func getProducts(completion: ([Product]?, Error?) -> Void) {
    // .. perform here data request

Defining a method like this makes it even harder to reason about the outcome on the caller’s side. Here value and error are optional, which requires us to perform an unwrap in any case. Unwrapping these optional results(value and error) in more code clutter does not help to improve readability.

How await works?

Await as the keyword stays to be used for calling async methods. Usually, we see them as best friends in Swift as one will never go without the other. You can basically say:

Await is awaiting a callback from its buddy async

Even though this sounds childish, it’s not a lie! If we take a look at an example by calling our earlier defined async throwing fetch products method:

do {
    let products = try await getProducts()
    print("Got \(products.count) products.")
} catch {
    print("Getting products failed with error \(error)")

We can note that the above code example is performing an asynchronous task. Using the await keyword, we tell our program to await a result from the getProducts method and only continue after a result arrives. This could either be an array of products or an error if anything went wrong while fetching the products.

What is structured concurrency?

Structured concurrency with async-await method calls makes it easier to understand the order of execution. Methods are linearly executed, one by one, without going back and forth like you would with closures.

To understand this better, we will take a look at how we would call the above code example before structured concurrency arrived:

// 1. Call the method

getProducts { result in

    // 3. The asynchronous method return

    switch result {

    case .success(let products):

        print(“Got \(products.count) products.”)

    case .failure(let error):

        print(“Getting products failed with error \(error)”)



// 2. The calling method exits

As you can see, the calling method returns before the products are fetched. In case a result is received, we go back into our flow within the completion callback. This is an unstructured order of execution and could be hard to understand. This is especially true if we would perform another asynchronous method within our completion callback which would add another closure callback:

// 1. Call the method
getProducts { result in
    // 3. The asynchronous method return
    switch result {
    case .success(let products):
        print("Got \(products.count) products.")
        // 4. Call the placed method
        placedProducts(products) { result in
            // 6. Placed method returns
            switch result {
            case .success(let products):
                print("Decoded \(products) products.")
            case .failure(let error):
                print("Decoding products failed with error \(error)")
        // 5. Fetch products method returns
    case .failure(let error):
        print("Getting products failed with error \(error)")
// 2. The calling method exits

Each completion callback (closure) adds another level of indentation, which makes it harder to follow the order of execution.

If we rewrite the above code using async-await syntax, we have a more readable piece of code, and also explains best what structured concurrency does:

do {
    // 1. Call the method
    let products = try await getProducts()
    // 2. Fetch products method returns
    // 3. Call the placed method
    let placedProducts = try await placedProducts(products)
    // 4. Placed method returns
    print("Got \(products.count) products.")
} catch {
    print("Getting products failed with error \(error)")
// 5. The calling method exits

The order of execution is linear, easy to follow and easy to reason about. Asynchronous calls will be easier to understand while we’re still performing sometimes complex asynchronous tasks.

Making Swift networking code more readable

Reading Time: 3 minutes

With Swift 5 a new type got introduced:

@frozen public enum Result<Success, Failure> where Failure : Error {

    /// A success, storing a `Success` value.
    case success(Success)

    /// A failure, storing a `Failure` value.
    case failure(Failure)

The Result type is an enum consisting of 2 cases. The success and the failure case. Each of them can hold a generic value. The failure case, however, is limited to Types extending the Error type.

Not a big deal? Sure, but it’s the little things which add up and make a difference in the long run.

Lately, I was migrating from SwiftyJSON to native JSON parsing. Each network call was implemented in the following way:

func fetchSomething(completion: @escaping (SomeReturnValue?, SomeError?) -> Void) {
    NetworkingTool.request { (response) in
        guard response.isValid
            else { completion(nil, .somethingBad); return }
        do {
            let returnValue = try SomeReturnValue(response: response)
            completion(returnValue, nil)
        } catch {
            completion(nil, .scarry)

Looks okayish. Good. So let’s use it:

fetchSomething { (result, error) in
    guard error == nil
        else { handleError(error: error); return }
    doSomething(result: result)

Ok. But how to implement the doSomething? With an optional? This can’t be right, right? Force unwrap the result? And what about the error case? Force unwrap it? Oh and wait, what about the case where neither a result nor an error is returned? Is this even a thing? Ok, let me look up the implementation…

So a tiny bit of ambiguity paired with different people working on different parts of the network stack for different features can cause a real heterogeneous system. (Which does not imply that this is a bad system!)

If the company you’re working for is in favour of code ownership, you may not encounter this one. But so far no company I worked for was about code ownership. It’s usually your code is my code is our code, comrade. Period. There are simply too many trucks outside.

As long as code ownership isn’t a thing and you do not want to spend time on endless syntax and architectural discussions with little benefit or enforce a (new) best practice on all of your colleagues. Again. It comes really handy to have a built-in Result type which is reasonably unambiguous.

And since we all know that we’re spending more time reading code than writing, this saves us all valuable time.

Dependency injection and how I use it in Vaccination iOS app

Reading Time: 5 minutes

In programming, dependency injection is a technique where one object serves dependencies to another object. The concept is that instead of the client object to decide what kind of service it will use, another object tells to the client what service he has to use.

We can see the dependency injection as a software pattern. The fundament of this pattern is passing the service or object to the client, instead of allowing the client to find or to build the service on his own. What is the advantage of using this pattern? The main pros of this pattern are the readability of the code and code reusability.

Dependency injection – Injector example

Dependency injection is one form of the broader technique of inversion of control. The client delegates the responsibility of providing dependencies to external code (the injector) (Figure 1). The client is not allowed to call the injector code; it is injecting code that constructs the services and calls the client to inject them. This means the client code does not need to know about the injecting code, how to construct the services or even which actual services it is using; the client only needs to know about the intrinsic interfaces of the services because these define how the client may use the services. This separates the responsibilities of use and construction.

Types of DI

There are 3 types of dependency injection:

  1. Constructor injection: the dependencies are provided through a class constructor
  2. Setter injection: the client exposes a setter method that the injector uses to inject the dependency
  3. Interface injection: the dependency provides an injector method that will inject the dependency into any client passed to it. Clients must implement an interface that exposes a setter method that accepts the dependency

Vaccination app example

In the iOS world, the constructor injection is known as an initializer-based injection. This concept is realized with injecting the dependency object (or service) during initialization of the client class and this dependency is consistent/unchangeable during the life cycle of the client object.

In the previous few months, I’ve worked on the vaccination iOS application for N47 and I’ve decided to use the popular MVVM pattern inside. In the core of this pattern is the dependency injection. The components of the pattern are Model, View, and ViewModel, and each component is responsible for a different thing in the app. The point is to make the code more modular and easy to test.

The ViewModel (VM) component is a structure that contains only the data needed by the View component. The View component is presenting the data injected by the ViewModel. The ViewModel at other side is created by injecting dependency from the Model component. The main advantage of the MVVM is that we are creating views that have only one goal – presenting data. The view itself is not aware of the other task like fetching, persisting, etc.

We can see the initializer-based injection in action with the real example used in the Vaccination Demo App of N47. Let’s see first how the Details ViewModel looks like:

struct VaccineDetailsViewModel {
    let title: String
    let description: String
    let date: String?

The vaccine details view only needs title, description, and date for the vaccine. It doesn’t need more information. On the other hand, the vaccine model can contain more details about the vaccine, but this information is useless for the View. Inside the view controller (View component) we define view model property and set it via controller initializer. We can see this in the code snippet below:

 var vacineViewModel: VaccineDetailsViewModel?

class func createController(viewModel: VaccineDetailsViewModel?) -> VaccinesDetailViewController {
        let controller = VaccinesDetailViewController(nibName: "VaccinesDetailViewController", bundle: nil)
        controller.vacineViewModel = viewModel

        return controller

This type of injection is preferable because it keeps us the safety of creating incomplete objects and with that, we will avoid coding mistakes.
So when I want to create a controller that will present the details for the vaccines and the scheduled vaccines I’m using injection via initializer in this way:

let details = VaccinesDetailViewController.createController(model: vaccination.createModel())

Other DI types in action…

In the Vaccination App, I’m also using Dependency Injection via setter creating the UITableView cells.

var vaccinationData: Vaccination? = nil {
        didSet {
            guard let vaccineId = vaccinationData?.vaccineId else { return }
            guard let vaccine = VaccineManager.sharedInstance.getVaccineById(vaccineId: vaccineId) else { return }
            let language = ModuleSharedPreferences.shared.language.rawValue
            let translation = vaccine.translations[language]
            vaccineTitleLabel.text = translation?.name
            vaccineApplyDateLabel.text = vaccinationData?.date

The code snippet above shows the vaccination data object that should be set with setter if we want the cell to be populated with data. Here is the code that will do the magic:

        let cell = tableView.dequeueReusableCell(withIdentifier: VaccinesTableViewCell.cellIdentifier, for: indexPath) as! VaccinesTableViewCell
        let vaccination = vaccinationList[indexPath.row]
        cell.vaccinationData = vaccination


Dependency injection is a powerful technique. Our code becomes more readable, reusable and easy for testing. We were able to see this technique in action in a real project and it was used within the popular design patterns MVVM. Using this technique we become sure that our components/services are completed, fully created before we start to use it.

Impressions from UIKonf Berlin 2019

Reading Time: 5 minutes

With little doubts in the beginning, big uncertainty and the questions in my head “Is this the right conference?”, “Should I’ve chosen another conference?”… But it happened and I’m satisfied at the end of the day with my choice. Some of my colleagues were surprised of my choice (Berlin), but yes, I can definetly say “It was not a mistake”.

So how was it?

Day 1: Social events

I’ve chosen to be with the walking tour group. There were possibilities to be in different groups like Bicycle group, Boat trip group… But this was my choice. We were split into two groups by 12 people and a local tour guide. We visited different spots, like the Memorial to the Murdered Jews of Europe, the popular Checkpoint Charlie, the Berlin French Cathedral, the Brandenburg Gate, etc…

The day finished in a big restaurant where we received our conference badges and promo materials of the conference. It was a very relaxed atmosphere and new chance to meet and introduce yourself to new developers. Some of the participants were professionally oriented and immediately started to talk about iOS topics. Some were on their way to the bar ordering german beers and the popular “wursts”. There also was a small group that played some kind of table tennis game. I’ve attended in all of these social activities.

Day 2: Opening day of the UIKonf 2019

After the opening words and the short introduction, the conference officially started. The first day consisted of 9 presentations. The strongest impression of the day was the presentation of Ellie Shin about Mock Generator for Swift and how they at Uber solved the problem of mocking. They optimized the app to build the mocks in around 10 seconds instead of the previous time needed; it was more than 1 hour.

One of the best presentations of the day was from the lovely Julietta Yaunches. She talked about consistency principles in programming, how to keep the coding style consistent and not make big changes every day, how to decide when to introduce something new in your code, etc.

It were good presentations from Kristina Fox about Internationalization of the iOS applications, and the opening presentation from Kaya Tomas about Accessibility and Inclusion in the apps. A topic I wasn’t aware before this conference.

I also have to mention the presentation of Glenna Buford about how to organize the network stack of your iOS application.

The day finished with a social event named Ambassador’s dinner. The local participants of the conference had the task to show the other participants (foreigners, including me) the typical restaurants and bars in Berlin. I was in a group that visited Hofbrau Munich Restaurant in Berlin. We had some good discussions with colleagues from all over the World. Throughout the evening we enojyed some tasty good german beer and pork meat.

Day 3: 2nd day of UIKonf 2019

The second day had 9 new presentations and 9 new speakers.

The best of the day in my opinion was the presentation of Kate Castellano. She talked about applications with backend driven UI.

Among the better presentations was of Neha Kulkarni about Advanced Colors in Swift.

I will also mention Erica Sadun. She talked about Swift Strings. In her presentation she showed best practices about using strings in Swift, interpolation of strings, etc.

Also there was a choice to visit 2 workshops on MyTaxi boat stage near the conference hall. I visited this stage and it was about the management process, recruiting and organizing the teams in MyTaxi company. The 2nd presentation was about Tips and Tricks they are using for testing their apps.

The day finished with a big party in a local restaurant near the river Spree. It was another good party. A lot of talks with colleagues, a lot of drinks, good atmosphere. All finished at 1:00 am.


I can freely say it was a good conference, perfectly organized… I didn’t regret in any moment that I’ve chosen this one in a bunch of other conferences. First of all, I’ve learned a lot of new things, I’ve heard about topics I wasn’t aware before, I saw how it would be to talk infront of 500 people.

Maybe in these events the most important thing is the social component. New faces, positive people, talks, sharing experiences, enlarge your professional contact list.

All 18 speakers

I’m not sure what was the intention of the organizers to have only women speakers. 18 speakers – 18 women. It was not explained, but we cannot complain because the speakers dealt with the challenge perfectly.

Worth the money, worth spending my time at UIKonf in Berlin. I have a feeling I will visit it for sure again in the future.