Refactoring in Swift: Typealias-ing

Typealiases can be used for so much more than just defining JSON as [String: Any]

typealias Swift = Apodidae. Kingfishers unrelated.

Classic Uses

You’ve probably seen or used one of these typaliases before:

// To give commonly used structures a name

typealias JSON = [String: Any] // To combine two or more protocols into a protocol composition

typealias UserAPI = GetUserInterface & UpdateUserInterface // To make a closure type more readable

typealias Callback = (() -> ())

This article is not about these cases — there’s a much simpler way typealiases can improve your code!

Unit Clarity

The other day I was writing a function that contained some business logic to calculate the number of calories in a meal. It ended up looking like this:

func calculate(carbs: Float, protein: Float, fat: Float) -> Float

Now, I know what units this function takes and returns, but will anyone else?

Consider TimeInterval . It’s really a Double , but whenever we see a TimeInterval we immediately know that the value is expected in seconds.

// UIView.h

func animate(withDuration duration: TimeInterval, animations: @escaping () -> Swift.Void)

My calculate function can be improved even before a documentation comment is added:

/// A weight representation in Grams

typealias Grams = Float /// Kilo-calories

typealias kCal = Float func calculate(carbs: Grams, protein: Grams, fat: Grams) -> kCal

Seems like those arguments aren’t in SI units after all!

Legacy APIs

There are many other similar uses for typaliasing. In our current project we’ve used them when integrating with an API that returns Boolean values as Integers (yes, 🙄). Here’s a model of what we get back from one of the endpoints:

struct BadgeInfo: Decodable {



let lunch_flg: Int // 0 or 1

let dinner_flg: Int // 0 or 1

let snack_flg: Int // 0 or 1

}

This is less than ideal, however we can make sure other developers who need to interact with these models are aware that these three properties can only ever be 0 or 1 without them having to check the model file for comments:

/// An integer that can only be zero or one (support for 🙄 API)

typealias BoolInt = Int struct BadgeInfo: Decodable {



let lunch_flg: BoolInt

let dinner_flg: BoolInt

let snack_flg: BoolInt

}

You can do this right now to your project — its a change in only one file, and, (in my opinion), it makes your code much more readable.

Membership Numbers

Another of my favorite typealias uses is keeping track of identifiers:

typealias MemberIdentifier = String

typealias PassengerIdentifier = String

If you can use these identifiers whenever a membership number is needed, it will be immediately obvious to anyone reading your code which identifier is needed for which argument.

// Before - are we linking with the membership id or passenger id?

func link(user: String, to flight: Flight) // After - aha! (of course, the argument name could be tidied too..)

func link(user: PassengerIdentifier, to flight: Flight)

Another welcome side-effect we found in this scenario was: our static mock API returned membership numbers as integers, however when we were first given access to the real mock we found that membership numbers were really strings. Changing the definition of MemberIdentifier from Int to String and then cleaning up a few compile errors was much quicker than making the change for every reference!

And many more

Typealiases are a language feature that just keep on giving. To finish this article, here’s some inspiration on how you can use typealises next time you’re writing a function: