Asynchronous Object Initialisation in Swift
Tags: swift, ios, programming, tutorial
Published: 2018-08-01 03:02:54

---
title: Asynchronous Object Initialisation in Swift
published: true
description: Like baby birdies, immutable objects bring joy. Let's initialise them asynchronously!
tags: swift, ios, programming, tutorial
cover_image: https://thepracticaldev.s3.amazonaws.com/i/7bvyaoeihdgganee10o6.jpg
---

Baby birds, rockets, freshly roasted coffee beans, and … immutable objects. What do all these things have in common? I love them.

An immutable object is one that cannot change after it is initialised. It has no variable properties. This means that when using it in a program, my pea brain does not have to reason about the state of the object. It either exists, fully ready to complete its assigned duties, or it does not.

Asynchronous programming presents a challenge to immutable objects. If the creation of an object requires network I/O, then we will have to unblock execution after we have decided to create the object.

As an example, let’s consider the [`Transaction`](https://github.com/amatino-code/amatino-swift/blob/master/Sources/Amatino/Transaction.swift) class inside Amatino Swift. [Amatino is a double entry accounting API](https://amatino.io), and [Amatino Swift](https://github.com/amatino-code/amatino-swift) allows macOS & iOS developers to build finance capabilities into their applications.

To allow developers to build rich user-interfaces, it is critical that `Transaction` operations be smoothly asynchronous. We can’t block rendering the interface while the Amatino API responds! To lower the cognitive load demanded by Amatino Swift, Transaction should be immutable.

We’ll use a simplified version of `Transaction` that only contains two properties: `transactionTime` and `description`. Let’s build it out from a simple synchronous case, to a full fledged asynchronous case.

```swift
class Transaction {
  let description: String
  let transactionTime: Date 
  
  init(description: String, transactionTime: Date) {
    self.description = description
    self.transactionTime = transactionTime
  }
}
```

So far, so obvious. We can instantly initialise `Transaction`. In real life, `Transaction` is not initialised with piecemeal values, it is initialised from decoded JSON data received from an HTTP request. That JSON might look like this:

```json
{
  "transaction_time": "2008-08",
  "description": "short Lehman Bros. stock"
}
```

And we can decode that JSON into our `Transaction` class like so:

```swift
/* Part of Transaction definition */
enum JSONObjectKeys: String, CodingKey {
  case txTime = "transaction_time"
  case description = "description"
}

init(from decoder: Decoder) throws {
  let container = try decoder.container(
    keyedBy: JSONObjectKeys.self
  )
  description = try container.decode(
    String.self,
    forKey: .description
  )
  let dateFormatter = DateFormatter()
  dateFormatter.dateFormat = "yyyy-MM" //...
  let rawTime = try container.decode(
    String.self,
    forKey: .txTime
  )
  guard let txTime: Date = dateFormatter.date(
    from: rawTime
  ) else {
    throw Error
  }
  transactionTime = txTime
  return
}
```

Whoah! What just happened! We decoded a JSON object into an immutable Swift object. Nice! That was intense, so lets take a breather and look at a cute baby bird:

{% youtube JQ_NV6Cf9qA %}

Break time is over! Back to it: Suppose at some point in our application, we want to create an instance of `Transaction`. Perhaps a user has tapped ‘save’ in an interface. Because the Amatino API is going to (depending on geography) take ~50ms to respond, we need to perform an asynchronous initialisation.

We can do this by giving our `Transaction` class a static method, like this one:

```swift
/* Part of Transaction definition */
static func create(
  description: String,
  transactionTime: Date,
  callback: @escaping (Error?, Transaction?) -> Void
) throws {
  /* dummyHTTP() stands in for whatever HTTP request
     machinery you use to make an HTTP request. */
  dummyHTTP() { (data: Data?, error: Error?) in
    guard error == nil else { 
      callback(error, nil)
      return
    }
    guard dataToDecode: Data = data else {
      callback(Error(), nil)
      return
    }
    let transaction: Transaction
    guard transaction = JSONDecoder().decode(
      Transaction.self,
      from: dateToDecode
    ) else {
      callback(Error(), nil)
      return
    }
    callback(nil, transaction)
    return
  }
}
```

This new `Transaction.create()` method follows these steps:

1. Accepts the parameters of the new transaction, and a function to be called once that transaction is available, the `callback(Error?:Transaction?)`. Because something might go wrong, this function might receive an error, (`Error?`) or it might receive a transaction (`Transaction?`)

2. Makes an HTTP request, receiving optional Data and Error in return, which are used in a closure. In this example, `dummyHTTP()` stands in for whatever machinery you use to make your HTTP requests. For example, check out [Apple’s guide to making HTTP requests in Swift](https://developer.apple.com/documentation/foundation/url_loading_system/fetching_website_data_into_memory)

3. Looks for the presence of an error, or the absence of data and, if they are found, calls back with those errors: callback(error, nil)

4. Attempts to decode a new instance of `Transaction` and, if successful, calls back with that transaction: `callback(nil, transaction)`

We can use the `.create()` static method like so:

```swift
Transaction.create(
  description: "Stash sweet loot",
  transactionTime: Date(),
  callback: { (error, transaction) in 
    // Guard against errors, then do cool stuff
    // with the transaction
})
```

The end result? An immutable object. We don’t have to reason about whether or not it is fully initialised, it either exists or it does not. Consider an alternative, wherein the `Transaction` class tracks internal state:

```swift
class Transaction {
  var HTTPRequestInProgress: bool
  var hadError: Bool? = nil
  var description: String? = nil
  var transactionTime: Date? = nil

  init(
    description: String,
    transactionTime: Date,
    callback: (Error?, Transaction?) -> Void
  ) {
    HTTPRequestInProgress = true
    dummyHTTP() { data: Data?, error: Error? in 
       /* Look for errors, try decoding, set
          `hadError` as appropriate */
       HTTPRequestInProgress = false
       callback(nil, self)
       return
    }
  }
}
```

Now we must reason about all sorts of new possibilities. Are we trying to utilise a `Transaction` that is not yet ready? Have we guarded against nil when utilising a `Transaction` that is ostensibly ready?  Down this path lies a jumble of `guard` statements, `if-else` clauses, and sad baby birdies.

Don’t make the baby birdies sad, asynchronously initialise immutable objects! 💕

###Further Reading

* The [full source code for the Amatino Swift `Transaction` class](https://github.com/amatino-code/amatino-swift/blob/master/Sources/Amatino/Transaction.swift), which is immutable and makes heavy use of asynchronous operations
* Apple’s guide to [making asynchronous HTTP requests in Swift](https://developer.apple.com/documentation/foundation/url_loading_system/fetching_website_data_into_memory)
* Apple’s guide to [encoding & decoding JSON data in Swift](https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types)

– [Hugh](https://twitter.com/hugh_jeremy)

*Originally posted on [hughjeremy.com](https://hughjeremy.com/blog/asynchronous-object-initialisation-in-swift/)*