Removing duplicate values from an array in Swift

Arrays in Swift can hold on to all kinds of data. A common desire developers have when they use arrays, is to remove duplicate values from their arrays. Doing this is, unfortunately, not trivial. Objects that you store in an array are not guaranteed to be comparable. This means that it's not always possible to determine whether two objects are the same. For example, the following model is not comparable:

struct Point {
  let x: Int
  let y: Int
}

However, a keen eye might notice that two instances of Point could easily be compared and two points that are equal would have the same x and y values. Before you can remove defaults from an array, you should consider implementing Equatable for the object you're storing in your array so we can use the == operator to compare them:

extension Point: Equatable {
  static func ==(lhs: Point, rhs: Point) -> Bool {
    return lhs.x == rhs.x && lhs.y == rhs.y
  }
}

Once you've determined that you're dealing with Equatable objects, you can define an extension on Array that will help you remove duplicate values as follows:

extension Array where Element: Equatable {
  func uniqueElements() -> [Element] {
    var out = [Element]()

    for element in self {
      if !out.contains(element) {
        out.append(element)
      }
    }

    return out
  }
}

This way of removing duplicates from your array is not very efficient. You have to loop through all of the elements in the array to build a new array and every call to contains loops through the out array. This isn't ideal and in some cases we can do better.

I say some cases because the more optimal solution requires that the element in your array conforms to Hashable. For the Point struct I showed you earlier this is easy to achieve. Since Point only has Int properties and Int is Hashable, Swift can synthesize an implementation of Hashable when we conform Point to Hashable:

extension Point: Hashable {}

If the elements in your array are already hashable, you don't have to declare this extension yourself.

For an array of hashable elements, we can use the following implementation of uniqueElements():

extension Array where Element: Hashable {
  func uniqueElements() -> [Element] {
    var seen = Set<Element>()
    var out = [Element]()

    for element in self {
      if !seen.contains(element) {
        out.append(element)
        seen.insert(element)
      }
    }

    return out
  }
}

This code looks very similar to the previous version but don't be fooled. It's much better. Note that I defined a Set<Element> in this updated implementation. A Set enforces uniqueness and allows us to look up elements in constant time. This means that seen.contains(element) doesn't have to loop over all elements in the set to find the element you're looking for. This is a buge improvement over the Equatable version of this algorithm because it removes an entire nested loop (which is hidden in contains) from our implementation. Note that the loop from this code can be cleaned up a bit with a compactMap instead of a for loop. This doesn't change the performance but I think it looks a bit nicer:

extension Array where Element: Hashable {
  func uniqueElements() -> [Element] {
    var seen = Set<Element>()

    return self.compactMap { element in
      guard !seen.contains(element)
        else { return nil }

      seen.insert(element)
      return element
    }
  }
}

Functionally these two implementations are the same, and they also have the same performance characteristics so pick whichever one you like.

There is one more way of implementing uniqueElements() for an array of Hashable elements that is even more efficient. It does come with one caveat though. When you use this last version, you might lose the order of your original array which means you should only use this version if you don't care about the ordering of your array:

extension Array where Element: Hashable {
  func unsortedUniqueElements() -> [Element] {
    let set = Set(self)
    return Array(set)
  }
}

By converting the array to a Set, all duplicate values are automatically dropped from the array. We can then convert the set back to an array to get an array with all duplicate values removed. Because sets don't enforce any ordering, you might lose the original order of your array. The previous two versions of uniqueElements preserved the ordering of your input. You should use this version if you need your array's order to be preserved. If you don't care about order and your elements are Hashable I would recommend to use the Set approach I showed last.

I hope this quick tip gave you some useful insights into arrays and how you can deduplicate them. If you have questions, feedback or alternative solutions don't hesitate to reach out on Twitter.

Profiling and debugging your Combine code with Timelane

When we write code, we write bugs. It's one of the laws of the universe that we can't seem to escape. The tools we have to discover, analyze and fix these bugs are extremely important because without good debugging tools we'd be poking at a black box until we kind of figure out what might be happening in our code. Debugging synchronous code is hard enough already, but once your code involves several streams of asynchronous work debugging becomes much harder because asynchronous code can be inherently hard to keep track of.

Combine code is asynchronous by nature. When you use Combine to receive or send values, you're dealing with an asynchronous stream of values that will (or will not) output information over time. One way to gain insight into what your Combine code is doing is to use Combine's print operator which will print information to Xcode's console. While this is fine if you're debugging one stream at a time, the console can become unwieldy quickly when you're logging information on multiple subscriptions or if you're logging lots of information that's not necessarily related to Combine.

In this week's blog post I'd like to take a closer look at Marin Todorov's super helpful Timeline instrument that helps you gain insight into Combine code by visualizing what your Combine code is doing in realtime. By the end of this post, you will know how to install, configure and use Timelane in your projects.

Preparing to use Timelane

To use Timelane, there are two things you need to do. First, you need to install the Instruments template that is used to visualize your data streams. Second, you need to add the TimelaneCore and TimelaneCombine dependencies to your project. Note that there is also a RxTimelane framework available that allows you to use Timelane to profile RxSwift code. In this post, I will focus on Combine but the RxSwift version works in the same manner as the Combine version.

To install the Timelane Instruments template, go to the Timelane releases page on Github and download the Timelane app zip file. Open the downloaded application and follow the installation instructions shown in the app:

Screenshot of the Timelane installer app

After installing the Instruments template, you can go ahead and open Xcode. The easiest way to integrate Timelane is through the Swift Package Manager. Open the project you want to use Timelane in and navigate to File -> Swift Packages -> Add Package Dependency.

Screenshot of the Xcode menu to access Swift Package Manager

In the pop-up that appears, enter the TimelaneCombine Github URL which is: https://github.com/icanzilb/TimelaneCombine.

Screenshot of the Add Package screen with the TimelaneCombine URL prefilled

Adding this package to your project will automatically pull down and install the TimelaneCombine and TimelaneCore packages in your project. If you're using Cocoapods or Carthage to manage your dependencies you can add the TimelaneCombine dependency to your Podfile or Cartfile as needed.

Debugging subscriptions with Timelane

Once you have all the required dependencies in place, you can begin profiling your Combine code quite easily. All you need to do is add a call to Timelane's lane operator after the publisher you want to debug and you're good to go. For example, if you have a publisher in your project that downloads data from the web and decodes it into a JSON model, you might use the following code to set up Timelane to make sure your code works as expected:

URLSession.shared.dataTaskPublisher(for: URL(string: "https://donnywals.com")!)
  .map(\.data)
  .decode(type: SomeModel.self, decoder: JSONDecoder())
  .lane("Decoding data")
  .sink(receiveCompletion: { _ in }, receiveValue: { value in
    print(value)
  })
  .store(in: &cancellables)

Note that most code can be written just like you would write it normally. All you need to do to profile your code is add a call to the lane operator and provide a name for the lane you want to visualize the publisher stream in. To debug this code, you need to run your app with Instruments enabled. To do this go to Product -> Profile or press cmd + i. When your project is compiled, Instruments will ask you to choose a template for your profiling session. Make sure you choose the Timelane template:

Instruments' template selection window

Instruments will open the template and you can start your profiling session by pressing the record button in the top left corner of the window. You will see that Timelane will immediately visualize your publishers in realtime. The output for the code above looks as follows:

Example of a single, simple Timelane log

You can see that the stream failed because the subscription finished with an error. You can even see why. The loaded data wasn't in the correct format. This makes sense because I loaded the homepage of this website, which is HTML and I tried to decode it as JSON.

It's possible to visualize multiple steps of a single chain of publishers to see where exactly things go wrong. For example, you can add the lane operators to every step of the chain I showed earlier:

URLSession.shared.dataTaskPublisher(for: URL(string: "https://donnywals.com")!)
  .lane("Fetching data")
  .map(\.data)
  .lane("Mapping response")
  .decode(type: SomeModel.self, decoder: JSONDecoder())
  .lane("Decoding data")
  .sink(receiveCompletion: { _ in }, receiveValue: { value in
    print(value)
  })
  .store(in: &cancellables)

If you'd run Instruments with this code, you would see the following output:

Screenshot of multiple subscription and event lanes

There are now multiple subscription lanes active and you can see that there are values for each subscription lane. Note that the two lanes we just added get canceled because decode fails. This is a detail in Combine that I would not have known about without profiling my code using Timeline. It might be an insignificant detail in the grand scheme of things but it's pretty neat either way.

In this case, you might not be interested in seeing a subscription lane for each lane operator you use. After all, all three lanes I created in the code I just showed you are tightly related to each other. And if any publisher in the chain throws an error, this error will travel through all downstream operators before it reaches the sink. This allows you to see exactly which publisher in the chain threw an error, but it also creates some visual noise that you may or may not be interested in. Here's an example of what happens when I replace the map from the example code with a tryMap and throw an error:

Screenshot of two lanes with errors

Timelane allows you to choose the lanes that it logs to. So in this case, it would make sense to only log subscription information for the last lane operator in the chain which is Decoding data. To do this, you can use the filter argument for lane:

URLSession.shared.dataTaskPublisher(for: URL(string: "https://donnywals.com")!)
  .lane("Fetching data", filter: [.event])
  .map(\.data)
  .lane("Mapping response", filter: [.event])
  .decode(type: SomeModel.self, decoder: JSONDecoder())
  .lane("Decoding data")
  .sink(receiveCompletion: { _ in }, receiveValue: { value in
    print(value)
  })
  .store(in: &cancellables)

By passing filter: [.event] to lane, you will only see events, or values, in Instruments and the subscription lane will online show the last lane from the code because that lane isn't filtered. By doing this you can limit the number of timelines that are shown in the subscription lane while still seeing all values that pass through your publisher chain.

Screenshot of a single subscription lane with multiple event lanes

Visualizing events only is especially useful for publishers that never complete (or fail) like NotificationCenter.Publisher or maybe a CurrentValueSubject that you're using to send an infinite stream of custom values through your application.

If you're using @Published properties in your app and you want to easily track them in Timeline, you can use the @PublishedOnLane property wrapper where you would normally use @Published. The @PublishedOnLane property wrapper uses an @Published property wrapper internally and overrides projectedValue to return a publisher that has a lane operator applied to it. In other words, you get all the behavior you'd expect from an @Published property with the logging superpowers from Timelane. Here's what it looks like when you use @PublishedOnLane in your code:

@PublishedOnLane("A property") var aProperty: String = "Default value"

If you want to use this property wrapper without having its subscriptions show up in the subscriptions lane, you can apply a filter as follows:

@PublishedOnLane("A property", filter: [.event]) var aProperty: String = "Default value"

The result of applying this filter is exactly the same as it is when you use the lane operator.

Caution:
The filter option for @PublishedOnLane was merged the day before I published this post so there's a chance that it's not yet available by the time you get to experiment with this post. Keep an eye on Timeline updates and make sure to try it again once a new version is released.

Examining values in Timelane

So far, I have mostly focussed on showing you how Timelane visualizes a publisher's lifecycle. Going from created to being canceled, erroring and completing. I haven't shown you that Timelane also provides insight into a publisher's output. Consider a publisher that updates every time a user types a character. This publisher is debounced to make sure we don't process values while the user is still typing:

usernameField.$value
  .lane("Username pre-debounce", filter: [.event])
  .debounce(for: 0.3, scheduler: DispatchQueue.main)
  .lane("Username", filter: [.event])
  .sink(receiveValue: { value in
    // handle value
  })
  .store(in: &cancellables)

By applying a lane before, and after the debounce it's possible to see exactly what I've typed, and what was sent to the sink. Examine the following screenshot:

An example of a debounced publisher with a duplicate value

By clicking the events lane, the bottom panel in Instruments shows an overview of all events that were logged per lane. Note that the string Test got delivered to the sink twice. The reason is that I hit backspace after typing but immediately typed another t. This means that we're processing the same output twice which could be wasteful. By applying the removeDuplicates operator after debounce, we can fix this:

usernameField.$value
  .lane("Username pre-debounce", filter: [.event])
  .debounce(for: 0.3, scheduler: DispatchQueue.main)
  .removeDuplicates()
  .lane("Username", filter: [.event])
  .sink(receiveValue: { value in
    // handle value
  })
  .store(in: &cancellables)

And if you look at the events view in Instruments again, you can see that the duplicate value is now gone:

An example of a publisher that uses removeDuplicates to prevent duplicate outputs

The ability to examine individual values through Instruments and Timelane is extremely useful to identify and fix problems or potential issues that you might not have discovered otherwise.

Note that the output in Instruments looks like this Optional("Test"). The output would look much nicer if we printed Test instead. You can achieve this by passing a transformValue closure to the lane operator. This closure is passed the value that Timelane will log to Instruments and you can modify this value by returning a new value from the closure:

usernameField.$value
  .lane("Username pre-debounce", filter: [.event])
  .debounce(for: 0.3, scheduler: DispatchQueue.main)
  .removeDuplicates()
  .lane("Username", filter: [.event], transformValue: { value in
    return value ?? ""
  })
  .sink(receiveValue: { value in
    // handle value
  })
  .store(in: &cancellables)

By returning value ?? "" the logged value is no longer an optional, and Timelane will log Test instead of Optional("Test"). You can also apply more elaborate transformations to the logged value. For example, you could write the following code to print Value is: Test instead of just the received value:

usernameField.$value
  .lane("Username pre-debounce", filter: [.event])
  .debounce(for: 0.3, scheduler: DispatchQueue.main)
  .removeDuplicates()
  .lane("Username", filter: [.event], transformValue: { value in
    return "Value is: \(value ?? "")"
  })
  .sink(receiveValue: { value in
    // handle value
  })
  .store(in: &cancellables)

The ability to transform the logged value can be very helpful if you want a little bit more control over what is logged exactly or if you want to make Timelane's logged values more readable which is especially useful if you're logging more complex objects than a String. For example, you might not want to log an entire User struct but instead only return its id or name property in the transformValue closure. It's entirely up to you to decide what you want to log.

In Summary

Being able to see what's going on inside of your application's asynchronous code is an invaluable debugging tool so the fact that Marin created and open-sourced Timelane is something that I am extremely grateful for. It makes debugging and understanding Combine code so much easier, and the fact that you get all of this information through a simple operator is somewhat mind-boggling.

The tool is still very young but in my opinion, Timelane is well on its way to become a standard debugging tool for RxSwift and Combine code. If you like Timelane as much as I do, be sure to share the love and let Marin know. And if you have any questions or feedback about this post, don't hesitate to reach out on Twitter.

What is @escaping in Swift?

If you've ever written or used a function that accepts a closure as one of its arguments, it's likely that you've encountered the @escaping keyword. When a closure is marked as escaping in Swift, it means that the closure will outlive, or leave the scope that you've passed it to. Let's look at an example of a non-escaping closure:

func doSomething(using closure: () -> Void) {
  closure()
}

The closure passed to doSomething(using:) is executed immediately within the doSomething(using:) function. Because the closure is executed immediately within the scope of doSomething(using:) we know that nothing that we do inside of the closure can leak or outlive the scope of doSomething(using:). If we'd make the closure in doSomething(using:) outlive or leave the function scope, we'd get a compiler error:

func doSomething(using closure: () -> Void) {
  DispatchQueue.main.async {
    closure()
  }
}

The code above will cause a compiler error because Swift now sees that when we call doSomething(using:) the closure that's passed to this function will escape its scope. This means that we need to mark this as intentional so callers of doSomething(using:) will know that they're dealing with a closure that will outlive the scope of the function it's passed to which means that they need to take precautions like capturing self weakly. In addition to informing callers of doSomething(using:) about the escaping closure, it also tells the Swift compiler that we know the closure leaves the scope it was passed to, and that we're okay with that.

You will commonly see escaping closures for functions that perform asynchronous work and invoke the closure as a callback. For example, URLSession.dataTask(with:completionHandler:) has its completionHandler marked as @escaping because the closure passed as completion handler is executed once the request completes, which is some time after the data task is created. If you write code that takes a completion handler and uses a data task, the closure you accept has to be marked @escaping too:

func makeRequest(_ completion: @escaping (Result<(Data, URLResponse), Error>) -> Void) {
  URLSession.shared.dataTask(with: URL(string: "https://donnywals.com")!) { data, response, error in
    if let error = error {
      completion(.failure(error))
    } else if let data = data, let response = response {
      completion(.success((data, response)))
    } else {
      assertionFailure("We should either have an error or data + response.")
    }
  }
}

Tip:
I'm using Swift's Result type in this snippet. Read more about it in my post on using Result in Swift 5.

Notice that in the code above the completion closure is marked as @escaping. It has to be because I use it in the data task's completion handler. This means that the completion closure won't be executed until after makeRequest(_:) has exited its scope and the closure outlives it.

In short, @escaping is used to inform callers of a function that takes a closure that the closure might be stored or otherwise outlive the scope of the receiving function. This means that the caller must take precautions against retain cycles and memory leaks. It also tells the Swift compiler that this is intentional. If you have any questions about this tip or any other content on my blog, don't hesitate to send me a Tweet.

What are computed properties in Swift and when should you use them?

One of Swift's incredibly useful features is its ability to dynamically compute the value of a property through a computed property. While this is a super handy feature, it can also be a source of confusion for newcomers to the language. A computed property can look a bit strange if you haven't seen one before; especially when you are learning about custom get and set closures for properties at the same time.

In this week's post, I would like to take some time to explain computed properties in-depth so you can begin using them in your codebase with confidence.

By the end of this post, you will have a solid understanding of how computed properties work, how and when they are executed and how you can begin using them in your code.

Understanding how a computed property works

A computed property in Swift is a property that can't be assigned a value directly. It calculates its current value based on a closure-like block of code that you return a value from. Let's look at a very basic example of a computed property:

struct User {
  var firstName: String
  var lastName: String

  var fullName: String {
    "\(firstName) \(lastName)"
  }
}

In this example, I have defined a fullName property on a struct that concatenates two strings as a computed property. Every time I access fullName, the code between the { and } is executed to determine what the value of fullName should be:

var user = User(firstName: "Donny", lastName: "Wals")
print(user.fullName) // "Donny Wals"
user.lastName = "Secret"
print(user.fullName) // "Donny Secret"

This is incredibly useful because the user's fullName will always reflect the combination of the user's current firstName and lastName properties.

Because a computed property isn't a stored property, it's also okay to declare a computed property in an extension of an object:

struct User {
  var firstName: String
  var lastName: String
}

extension User {
  var fullName: String {
    "\(firstName) \(lastName)"
  }
}

A computed property in Swift is a get-only property because it does not have any storage that you can assign a value to. This means that the following code where we attempt to assign a value to one of our computed properties is not valid Swift:

var user = User(firstName: "Donny", lastName: "Wals")
user.fullName = "New Name" //  Cannot assign to property: 'fullName' is a get-only property

In my example, I was able to write my computed property in a single statement so I omitted the return keyword. A computed property essentially follows the same rules as functions and closures do, so you can also have multiple lines in a computed property:

extension User {
  var fullName: String {
    if firstName.isEmpty {
      return "<unkown> \(lastName)"
    } else if lastName.isEmpty {
      return "\(firstName) <unkown>"
    } else {
      return "\(firstName) \(lastName)"
    }
  }
}

When you write a computed property, you can write it with two different notations. One is the notation I have been using so far. The other is a slightly more specific version:

extension User {
  var fullName: String {
    get {
      "\(firstName) \(lastName)"
    }
  }
}

In this notation, you explicitly define the getter for fullName. It's not common to write your property like this if you don't also specify the setter for a property.

If your implementing conformance to a protocol, a computed property can be used to satisfy a protocol requirement that uses a get property:

protocol NamedObject {
  var firstName: String { get set }
  var lastName: String { get set }
  var fullName: String { get }
}

extension User: NamedObject {}

The User object I've shown at the start of this post satisfies all requirements from the NamedObject protocol. Because fullName is a get property, you can use a constant (let) to satisfy this requirement, but a computed property would work as well and be more fitting in this case. In fact, because we can declare computed properties on extensions, it's possible to provide a default implementation for fullName that's defined on NamedObject:

extension NamedObject {
  var fullName: String { "\(firstName) \(lastName)" }
}

Keep in mind that a computed property is evaluated every time you access it. This means that its value is always up to date, and computed based on the current state of the program. So in the case of fullName, its value will always be based on the current values of firstName and lastName, no matter what.

While this is convenient in many cases, it can also get you in trouble if you're not careful. Let's go over some best practices to help you make sure that your computed property usage is appropriate and efficient.

Best practices when using computed properties

Knowing when you might want to use a computed property is key if you want to use them efficiently. In the previous section, I've shown you how computed properties work. A keen eye may have noticed that a computed property acts a lot like a function that takes no argument and returns a single value:

extension User {
  var fullName: String {
    "\(firstName) \(lastName)"
  }

  func fullName() -> String {
    "\(firstName) \(lastName)"
  }
}

I have found that a good rule of thumb for computed properties and this kind of function is to try and decide what the function does. If your function takes no arguments and only reads some values from its current environment without any side-effects, you're looking at a good candidate for a computed property. The lack of side-effects is absolutely crucial here. Accessing a property should not produce any side-effects. A side-effect, in this case, could be changing a property, kicking off a network call, reading from a database or really anything more than just passively reading the current state of the program.

Because a computed property is recomputed every time it's accessed it's important that you make sure your implementation of a computed property is efficient. If the code needed to compute a value is complex and potentially slow, consider moving this code into a function with a completion closure and performing the work asynchronously. Performing work asynchronously is something you cannot do in a computed property.

Another important consideration to take into account related to the property being recomputed for every access is to make sure you don't confuse a computed property with a lazy property that is initialized with a closure:

extension User {
  var fullName: String {
    "\(firstName) \(lastName)"
  }

  lazy var expensiveProperty: String = {
    // expensive work
    return resultOfExpensiveWork
  }()
}

The expensiveProperty in this example is initialized using the closure expression I assigned to it. It's also lazy which means it won't be evaluated until expensiveProperty is accessed for the first time. This is very different from how a computed property works. The lazy property is only evaluated and initialized once. Subsequent access of the lazy property won't re-execute the expensive work. It's important to understand this difference because performing expensive or redundant work in a computed property can cause serious performance problems that can be very frustrating to track down.

In summary

In this post, I explained how computed properties work in Swift, and how you can use them effectively. I explained how you can declare computed properties in your codebase, and I've shown you that you can use computed properties to satisfy protocol requirements.

I moved on to explain some best practices surrounded computed properties and I've shown you that you can sometimes replace simple functions with computed properties to make your code easier to read. I also showed you the difference between a lazy property and a computed property, and I explained that it's important to understand the differences between the two to avoid potential performance issues.

If you have any questions or feedback about this post, don't hesitate to send me a Tweet.

Reading and writing Property List files with Codable in Swift

You have probably seen and used a property list file at some point in your iOS journey. I know you have because every iOS app has an Info.plist file. It's possible to create and store your own .plist files to hold on to certain data, like user preferences that you don't want to store in UserDefaults for any reason at all. In this week's Quick Tip you will learn how you can read and write data from and to property list files using Swift's Codable protocol.

Defining a model that can be stored in a property list

Because Swift has special PropertyListEncoder and PropertyListDecoder objects, it's possible to define the model that you want to store in a property list using Codable:

struct APIPreferences: Codable {
  var apiKey: String
  var baseURL: String
}

This model is trivial but you can create far more complex models if you want. Any model that conforms to Codable can be used with property lists. If you haven't worked with Codable before, check out this post from my Antoine van der Lee to get yourself up to speed. His post is about JSON parsing, but everything he writes about defining models applies to property lists as well.

Loading a model from a property list

We can load plist files from the filesystem using the FileManager object. Let's dive right in with some code; this is a Quick Tip after all.

class APIPreferencesLoader {
  static private var plistURL: URL {
    let documents = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    return documents.appendingPathComponent("api_preferences.plist")
  }

  static func load() -> APIPreferences {
    let decoder = PropertyListDecoder()

    guard let data = try? Data.init(contentsOf: plistURL),
      let preferences = try? decoder.decode(APIPreferences.self, from: data)
      else { return APIPreferences(apiKey: "", baseURL: "") }

    return preferences
  }
}

I defined a simple class here because this allows me to use the APIPreferenceLoader in a brief example at the end of this post.

The plistURL describes the location of the property list file on the file system. Since it's a file that we want to create and manage at runtime, it needs to be stored in the documents directory. We could store an initial version of the plist file in the bundle but we'd always have to copy it over to the documents directory to update it later because the bundle is read-only. You might use the following code to perform this copy step:

extension APIPreferencesLoader {
  static func copyPreferencesFromBundle() {
    if let path = Bundle.main.path(forResource: "api_preferences", ofType: "plist"),
      let data = FileManager.default.contents(atPath: path),
      FileManager.default.fileExists(atPath: plistURL.path) == false {

      FileManager.default.createFile(atPath: plistURL.path, contents: data, attributes: nil)
    }
  }
}

This code extracts the default preferences from the bundle and checks whether a stored property list exists in the documents directory. If no file exists in the documents directory, the data that was extracted from the bundled property list is copied over to the path in the documents directory so it can be modified by the application later.

The load() method from the initial code sample uses a PropertyListDecoder to decode the data that's loaded from the property list in the bundle into the APIPreferences model. If you're familiar with decoding JSON in Swift, this code should look familiar to you because it's the exact same code! Convenient, isn't it?

If we couldn't load the property list in the documents directory, or if the decoding failed, load() returns an empty object by default.

Writing a model to a property list

If you have a model that conforms to Codable as I defined in the first section of this tip, you can use a PropertyListEncoder to encode your model into data, and you can use FileManager to write that data to a plist file:

extension APIPreferencesLoader {
  static func write(preferences: APIPreferences) {
    let encoder = PropertyListEncoder()

    if let data = try? encoder.encode(preferences) {
      if FileManager.default.fileExists(atPath: plistURL.path) {
        // Update an existing plist
        try? data.write(to: plistURL)
      } else {
        // Create a new plist
        FileManager.default.createFile(atPath: plistURL.path, contents: data, attributes: nil)
      }
    }
  }
}

This code checks whether our property list file exists in the documents directory using the plistURL that I defined in an earlier code snippet. If this file exists, we can simply write the encoded model's data to that file and we have successfully updated the property list in the documents directory. If the property list wasn't found in the documents directory, a new file is created with the encoded model.

Trying out the code from this post

If you've been following along, you can try the property list reading and writing quite easily with SwiftUI:

struct ContentView: View {
  @State private var preferences = APIPreferencesLoader.load()

  var body: some View {
    VStack {
      TextField("API Key", text: $preferences.apiKey)
      TextField("baseURL", text: $preferences.baseURL)
      Button("Update", action: {
        APIPreferencesLoader.write(preferences: self.preferences)
      })
    }
  }
}

If you enter some data in the text fields and press the update button, the data you entered will be persisted in a property list that's written to the document directory. When you run this example on your device or in the simulator you will find that your data is now persisted across launches.

In Summary

Because Swift contains a PropertyListEncoder and a PropertyListDecoder object, it's fairly simple to create models that can be written to a property list in Swift. This is especially true if you're already familiar with Codable and the FileManager utility. If you're not very experienced with these technologies, I hope that this Quick Tip provided you with some inspiration and ideas of what to look for, and what to explore.

If you have any feedback about this tip, or if you want to reach out to me don't hesitate to send me a tweet!

Using Result in Swift 5

As soon as Swift was introduced, people were adding their own extensions and patterns to the language. One of the more common patterns was the usage of a Result object. This object took on a shape similar to Swift's Optional, and it was used to express a return type that could either be a success or a failure. It took some time, but in Swift 5.0 the core team finally decided that it was time to adopt this common pattern that was already used in many applications and to make it a part of the Swift standard library. By doing this, the Swift team formalized what Result looks like, and how it works.

In today's post, my goal is to show you how and when Result is useful, and how you can use it in your own code. By the end of this post, you should be able to refactor your own code to use the Result object, and you should be able to understand how code that returns a Result should be called.

Writing code that uses Swift's Result type

Last week, I wrote about Swift's error throwing capabilities. In that post, you saw how code that throws errors must be called with a special syntax, and that you're forced to handle errors. Code that returns a Result is both very different yet similar to code that throws at the same time.

It's different in the sense that you can call code that returns a Result without special syntax. They're similar in the sense that it's hard to ignore errors coming from a Result. Another major difference is how each is used in an asynchronous environment.

If your code runs asynchronously, you can't just throw an error and force the initiator of the asynchronous work to handle this error. Consider the following non-functional example:

func loadData(from url: URL, completion: (Data?) -> Void) throws {
  URLSession.shared.dataTask(with: url) { data, response, error in
    if let error = error {
      throw error
    }

    if let data = data {
      completion(data)
    }
  }.resume()
}

Other than the fact that Swift won't compile this code because the closure that's passed as the data task's completion handler isn't marked as throwing, this code doesn't make a ton of sense. Let's examine what the call site for this code would potentially look like:

do {
  try loadData(from: aURL) { data in
    print("fetched data")
  }

  print("This will be executed before any data is fetched from the network.")
} catch {
  print(error)
}

This isn't useful at all. The idea of using try and throwing errors is that the code in the do block immediately moves to the catch when an error occurs. Not that all code in the do is executed before any errors are thrown, because the data task in loadData(from:completion:) runs asynchronously. In reality, the error that's potentially thrown in the data task's completion handler never actually makes it out of the completion handler's scope. So to summarize this paragraph, it's safe to say that errors thrown in an asynchronous environment never make it to the call-site.

Because of this, Swift's error throwing doesn't lend itself very well for asynchronous work. Luckily, that's exactly where Swift's Result type shines.

A Result in Swift is an enum with a success and failure case. Each has an associated value that will hold either the success value or an error if the result is a failure. Let's look at Result's definition real quick:

/// A value that represents either a success or a failure, including an
/// associated value in each case.
@frozen
public enum Result<Success, Failure: Error> {
  /// A success, storing a `Success` value.
  case success(Success)

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

The real definition of Result is much longer because several methods are implemented on this type, but this is the most important part for now.

Let's refactor that data task from before using Result so it compiles and can be used:

func loadData(from url: URL, completion: (Result<Data?, URLError>) -> Void) throws {
  URLSession.shared.dataTask(with: url) { data, response, error in
    if let urlError = error as? URLError {
      completion(.failure(urlError))
    }

    if let data = data {
      completion(.success(data))
    }
  }.resume()
}

loadData(from: aURL) { result in
  // we can use the result here
}

Great, we can now communicate errors in a clean manner to callers of loadData(from:completion:). Because Result is an enum, Result objects are created using dot syntax. The full syntax here would be Result.failure(urlError) and Result.success(data). Because Swift knows that you're calling completion with a Result, you can omit the Result enum.

Because the completion closure in this code takes a single Result argument, we can express the result of our work with a single object. This is convenient because this means that we don't have to check for both failure and success. And we also make it absolutely clear that a failed operation can't also have a success value. The completion closure passed to URLSession.shared.dataTask(with:completionHandler:) is far more ambiguous. Notice how the closure takes three arguments. One Data?, one URLResponse? and an Error?. This means that in theory, all arguments can be nil, and all arguments could be non-nil. In practice, we won't have any data, and no response if we have an error. If we have a response, we should also have data and no error. This can be confusing to users of this code and can be made cleaner with a Result.

If the data task completion handler would take a single argument of type Result<(Data, URLResponse), Error>. It would be very clear what the possible outcomes of a data task are. If we have an error, we don't have data and we don't have a response. If the task completes successfully, the completion handler would receive a result that's guaranteed to have data and a response. It's also guaranteed to not have any errors.

Let's look at one more example expressing the outcome of asynchronous code using Result before I explain how you can use code that provides results with the Result type:

enum ConversionFailure: Error {
  case invalidData
}

func convertToImage(_ data: Data, completionHandler: @escaping (Result<UIImage, ConversionFailure>) -> Void) {
  DispatchQueue.global(qos: .userInitiated).async {
    if let image = UIImage(data: data) {
      completionHandler(.success(image))
    } else {
      completionHandler(.failure(ConversionFailure.invalidData))
    }
  }
}

In this code, I've defined a completion handler that takes Result<UIImage, ConversionFailure> as its single argument. Note that the ConversionFailure enum conforms to Error. All failure cases for Result must conform to this protocol. This code is fairly straightforward. The function I defined takes data and a completion handler. Because converting data to an image might take some time, this work is done off the main thread using DispatchQueue.global(qos: .userInitiated).async. If the data is converted to an image successfully, the completion handler is called with .success(image) to provide the caller with a successful result that wraps the converted image. If the conversion fails, the completion handler is called with .failure(ConversionFailure.invalidData) to inform the caller about the failed image conversion.

Let's see how you could use the convertToImage(_:completionHandler:) function, and how you can extract the success or failure values from a Result.

Calling code that uses Result

Similar to how you need to do a little bit of work to extract a value from an optional, you need to do a little bit of work to extract the success or failure values from a Result. I'll start with showing the simple, verbose way of extracting success and failure from a Result:

let invalidData = "invalid!".data(using: .utf8)!
convertToImage(invalidData) { result in
  switch result {
  case .success(let image):
    print("we have an image!")
  case .failure(let error):
    print("we have an error! \(error)")
  }
}

This example uses a switch and Swift's powerful pattern maching capabilities to check whether result is .success(let image) or .failure(let error). Another way of dealing with a Result is using its built in get method:

let invalidData = "invalid!".data(using: .utf8)!
convertToImage(invalidData) { result in
  do {
    let image = try result.get()
    print("we have an image!")
  } catch {
    print("we have an error \(error)")
  }
}

The get method that's defined of Result is a throwing method. If the result is successful, get() will not throw an error and it simply returns the associated success value. In this case that's an image. If the result isn't success, get() throws an error. The error that's thrown by get() is the associated value of the Result object's .failure case.

Both ways of extracting a value from a Result object have a roughly equal amount of code, but if you're not interested in handling failures, the get() method can be a lot cleaner:

convertToImage(invalidData) { result in
  guard let image = try? result.get() else {
    return
  }

  print("we have an image")
}

If you're not sure what the try keyword is, make sure to check out last week's post where I explain Swift's error throwing capabilities.

In addition to extracting results from Result, you can also map over it to transform a result's success value:

convertToImage(invalidData) { result in
  let newResult = result.map { uiImage in
    return uiImage.cgImage
  }
}

When you use map on a Result, it creates a new Result with a different success type. In this case, success is changed from UIImage to CGImage. It's also possible to change a Result's error:

struct WrappedError: Error {
  let cause: Error
}

convertToImage(invalidData) { result in
  let newResult = result.mapError { conversionFailure in
    return WrappedError(cause: conversionFailure)
  }
}

This example changes the result's error from ConversionError to WrappedError using mapError(_:).

There are several other methods available on Result, but I think this should set you up for the most common usages of Result. That said, I highly recommend looking at the documentation for Result to see what else you can do with it.

Wrapping a throwing function call in a Result type

After I posted my article on working with throwing functions last week, it was pointed out to me by Matt Massicotte that there is a cool way to initialize a Result with a throwing function call with the Result(catching:) initializer of Result. Let's look at an example of how this can be used in a network call:

func loadData(from url: URL, _ completion: @escaping (Result<MyModel, Error>) -> Void) {
  URLSession.shared.dataTask(with: url) { data, response, error in
    guard let data = data else {
      if let error = error {
        completion(.failure(error))
        return
      }

      fatalError("Data and error should never both be nil")
    }

    let decoder = JSONDecoder()

    let result = Result(catching: {
      try decoder.decode(MyModel.self, from: data)
    })

    completion(result)
  }
}

The Result(catching:) initializer takes a closure. Any errors that are thrown within that closure are caught and used to create a Result.failure. If no errors are thrown in the closure, the returned object is used to create a Result.success.

In Summary

In this week's post, you learned how you can write asynchronous code that exposes its result through a single, convenient type called Result. You saw how using Result in a completion handler is clearer and nicer than a completion handler that takes several optional arguments to express error and success values, and you saw how you can invoke your completion handlers with Result types. You also learned that Result types have two generic associated types. One for the failure case, and one for the success case.

You also saw how you can call out to code that exposes its result through a Result type, and you learned how you can extract and transform both the success and the failure cases of a Result.

If you have any feedback or questions for me about this post or any of my other posts, don't hesitate to send me a Tweet.

Using KeyPaths as functions in Swift 5.2

One of Swift 5.2's new features is the ability to use KeyPaths as functions. This can be extremely useful in cases where you'd only return the value of a certain KeyPath in a closure. Let's look at a pre-Swift 5.2 example where this is the case:

// Swift 5.1 and earlier
struct User {
  let id: UUID
  let name: String
  let age: Int
}

func extractUserIds(from users: [User]) -> [UUID] {
  return users.map { $0.id }
}

This code should look familiar to you. It's likely that you've written or seen something like this before. This code transforms an array of User objects into an array of UUID objects using map(_:) by returning $0.id for every user in the array.

In Swift 5.2, it's possible to achieve the same with the following code;

func extractUserIds(from users: [User]) -> [UUID] {
  return users.map(\.id)
}

This is much cleaner, and just as clear. We want to map over the user array with the \.id KeyPath which means that we'll end up with an array of UUID. Just like we did before.

You can use this Swift feature in all places where a single-argument closure is used, and where the KeyPath's return type matches that of the closure. We could write the following code to filter an array of users and extract only users that are 21 and up:

extension User {
  var isTwentyOneOrOlder: Bool {
    return age >= 21
  }
}

func extractUsers(_ users: [User]) -> [User] {
  return users.filter(\.isTwentyOneOrOlder)
}

Swift's filter(_:) takes a closure that accepts a single argument (in this case User), and the closure must return a Bool. So by adding a computed property to User that is a Bool, we can use its KeyPath to filter the array of users. Pretty neat, right?

Tip:
Other Swift 5.2 features that I wrote about are callAsFunction and the ability to pass default values to subscript arguments.

Using try catch in Swift

In Swift 2.0, Apple introduced the throws keyword in Swift. This addition to Swift language added the ability for developers to write code that clearly communicates that an error might occur during the execution of a code path, and it forces the caller of that code to handle, or explicitly ignore the error in-place. In this post I will show you what the throws keyword is exactly, and how you can deal with errors in your codebase.

Working with code that throws errors

If you've worked with JSONDecoder in Swift, you have already experienced code that can throw an error. Let's look at a quick example to refresh your mind.

do {
  let data = "hello, world".data(using: .utf8)!
  let decoder = JSONDecoder()
  let string = try decoder.decode(String.self, from: data)
} catch { error in
  print("something went wrong!")
  print(error)
}

This code is a very basic example of how you can work with code that might throw an error. When you call a function that can throw an error, you must prefix this invocation with the try keyword. You can't just do this anywhere. Calling out to code that might throw an error must occur in a do {} catch {} block. The try prefixed code must be in the do portion of the block. You can have more than one try prefixed method call in a single do block. When any of those method calls throws an error, execution is immediately moved to the catch part.

The catch block receives a single argument, which is the Error that was thrown in your do block. In Swift, Error is a protocol that is used to represent errors. It's possible to specialize your catch block to make it catch only a specific kind of error. If you do this, you still need a general purpose catch that will catch all other Error types. Let's look at an example:

do {
  let data = "hello, world".data(using: .utf8)!
  let decoder = JSONDecoder()
  let string = try decoder.decode(String.self, from: data)
} catch is DecodingError {
  print("something went wrong while decoding!")
} catch { error
  print("something went wrong!")
  print(error)
}

You can use pattern matching to specialize catch clauses for an entire category of errors, or a specific error (catch is MyError.someCase). Doing this can be convenient if you have special handling paths for certain errors, but you do lose access to the original error. The final catch-all is invoked if none of the specialized catch blocks match the thrown error.

There are cases when you might make the decision that you don't want to handle any thrown errors. In these cases, you can use the try? prefix instead of try. When you call a throwing function with try?, the function's return type becomes an Optional that's nil when an error was thrown. Let's look at an example:

enum MyError: Error {
  case myErrorCase
}

func doSomething() throws {
  throw MyError.myErrorCase
}

print(try? doSomething())

This example is pretty worthless in a real codebase, but it illustrates my point nicely. The doSomething() function doesn't return anything. This means that it returns Void, which can also be written as an empty tuple (()). The printed result of this code is nil. If you comment out the throw line from doSomething(), the printed output is Optiona(()). In other words, an optional with Void as its value. If a function has a return value and you call it with try?, the result is also optional. Let's look at another example:

let data = "hello, world".data(using: .utf8)!
let decoder = JSONDecoder()
let string = try? decoder.decode(String.self, from: data)

print(string)

If you run this code in a Playground, you'll find that the printed value is nil. The provided data isn't valid JSON, so it fails to decode. But because we call decoder.decode(_:from:) with try?, the error is hidden and decode(_:from:) returns an optional value instead of its normal non-optional value.

If you call a throwing function that returns an optional with try?, you might expect a nested optional to be returned. After all, the function itself returns an optional and the return type of a throwing function is wrapped by an optional when you call it with try?. This means that a function that returns String returns String? when you call it with try?. However, if you call a function that returns String?, it doesn't return String??. Swift will automatically work around this nested optional, which is both a blessing and a curse. Consider the following code:

let string = try? returnsOptionalString()

if string == nil {
  // why is string nil? Is it due to an error? Or did the function execute successfully and we just got back nil without encountering any errors?
}

While the code above might be more convenient to write than a do {} catch {} block, you lose all error-related information. And in cases where code returns an optional, you don't know whether you received nil because of an error. You should only use try? if you truly don't care about handling errors, or knowing whether an error occurred at all.

There is one more way to deal with code that can throw errors. You can use try! in cases where you're absolutely sure that your code shouldn't throw an error, and you want your app to crash if it does. This flavor of try should be used sparingly, and preferably not at all. Let's look at one last example:

enum MyError: Error {
  case myErrorCase
}

func doSomething() throws {
  throw MyError.myErrorCase
}

try! doSomething() // 

This code would crash at runtime. doSomething() always throws an error, and by calling it with the try! prefix we tell Swift that we don't expect doSomething() to actually throw an error. And when it does, execution of the program should halt, and the app should crash. This is quite radical and, again, should be used sparingly.

Throwing errors in your own code

Sometimes, the code you write needs a way to express that something went wrong and execution of that code path needs to stop immediately with an error. If the error is recoverable, you might have a good candidate for a throwing function on your hands. When I say that an error is recoverable, I mean that the error didn't occur due to a programming error (like accessing an out of bounds index in an array for example) and that the program isn't in an unexpected or invalid state. It might simply mean that something went wrong.

For example, when you try to decode invalid JSON using a JSONDecoder, that's not considered an error that's severe enough to crash the app. Instead, an error is thrown to let you know that something went wrong. This is an important distinction, and trying to decode invalid JSON should never crash the application. At least not in the JSONDecoder. You're free to crash your app is a decoding error occurs if you want but I'd strongly advise you not to. Especially if you're loading JSON from a webserver.

When you're writing your own code, you might want to throw an error of your own. You already saw how to do this in the previous section in the doSomething function:

func doSomething() throws {
  throw MyError.myErrorCase
}

Functions that can throw an error must have the throws keyword appended to their signature, before the return type. Here's what a function signature for a throwing function with a return type looks like:

func returnsOptionalString() throws -> String? {
  // do work
}

When you're writing code in a so-called throwing function, you can call methods that throw errors without using a do {} catch {} block:

func decodeJSON(_ data: Data) throws -> String {
  let decoder = JSONDecoder()
  let decodedString = try decoder.decode(String.self, from: data)

  return decodedString
}

This code is okay because Swift knows that decodeJSON(_:) might encounter and throw an error. When the JSON decoding fails in this function, the thrown error is passed up to the caller of decodeJSON because it's marked as throwing with the throws keyword. When this function is called from another function that's throwing, that function will also forward the error up to its caller. The error will be passed up all the way to a caller that's not part of a throwing function.

There is one more error throwing related keyword that I want to show you. It's called rethrows. The rethrows keyword is used for functions that don't directly throw an error. Instead, the functions take a closure argument where the closure might throw instead of the function itself. Let's look at an example of a function that takes a throwing closure without rethrows:

func execute(_ closure: (() throws -> Void)) throws {
  try closure()
}

do {
  try execute {
    print("hello!")
  }

  try execute { 
    throw MyError.myErrorCase
  }
} catch {
  print(error)
}

In the code above I have defined an execute function. This function takes a single closure argument, and all it does is execute the closure immediately. Nothing fancy. You'll see that both execute(_:) and the closure it receives are marked with throws. It's important to understand that marking a function as throwing does not mean that the function is guaranteed to throw an error. All we're saying is that it might. This is especially relevant for the closure argument. The closure that's passed might not even be capable of throwing an error, just like the first call to execute(_:) in this example. Even though we know that this closure never throws an error, and the compiler also knows it, we must mark the call to execute(_:) with try because that function itself might throw an error.

We can clean this code up a little bit by declaring execute(_:) as rethrowing rather than throwing:

func execute(_ closure: (() throws -> Void)) rethrows {
  try closure()
}

execute {
  print("hello!")
}

do {
  try execute {
    throw MyError.myErrorCase
  }
} catch {
  print(error)
}

Because execute(_:) is now rethrowing, the Swift compiler can verify whether a code path might throw an error, and it will allow you to call execute(_:) without try if it can prove that the closure you passed to execute(_:) doesn't throw an error. Quite neat right?

You'll find rethrows in several places in the Swift standard library. For example, map(_:) is marked with rethrows because the closure you supply to map(_:) is allowed to throw errors if needed:

let mapped: [Int] = try [1, 2, 3].map { int in
  if int > 3 {
    throw MyError.intLargerThanThree
  }

  return int * 2
}

This probably isn't how you commonly use map(_:) because typically the closure passed to this function doesn't throw. But now you know that you're allowed to throw errors while mapping, and you also know why you're not forced to mark every call to map(_:) with try.

Note that the execution of any throwing method or closure is halted immediately when an error is thrown:

func doSomething() throws {
  throw MyError.myErrorCase

  print("This is never printed")
}

Throwing an error is a strong signal that something's wrong, and there's no point in fully executing the current code path. The error is sent to the caller of the throwing function, and it must be handled or forwarded from there. If you throw an error in a function that should return something, the function will not actually return anything. Instead, your code switches to the catch part of the do {} catch {} block immediately where you can handle the error. The exception here is when the called of your throwing function calls it with try? or try! like I explained in the previous section.

In Summary

In this post, I've shown you how you can deal with functions that can throw errors in Swift. You saw how you can call functions that are marked as throwing, how you can tell Swift you're not interested in handling an error and how you can tell Swift that you're absolutely sure a certain call will never actually throw an error at runtime.

After that, I moved on to show you how you can throw errors from your own code, what the rethrows keyword is and when it's useful.

If you have any feedback for me about this post, or if you have any questions, don't hesitate to reach out to me on Twitter.

Adding default values to subscript arguments in Swift 5.2

The ability to define custom subscripts in Swift is really powerful. It allows us to write very natural and concise code. Consider the following example of a Grid with a custom subscript:

struct Grid {
  let items : [[GridItem]]

  subscript(x x: Int, y y: Int) -> GridItem? {
    guard !items.isEmpty, (items.startIndex...items.index(before: items.endIndex)).contains(x)
      else { return nil }

    let row = items[x]

    guard !row.isEmpty, (row.startIndex...row.index(before: row.endIndex)).contains(y)
      else { return nil }

    return row[y]
  }
}

Note that subscripts don't use labels by default. To make a subscript use labels, you need to manually declare the subscript label twice like I did in the code above (x x: Int and y y: Int). A subscript like the above would be used as follows:

let item = grid[x: 10, y: 2]

This example works fine in Swift 5.1 and earlier versions in Swift. New in Swift 5.2 is the ability to provide default values for subscript arguments:

struct Grid {
  let items : [[GridItem]]

  subscript(x x: Int = 0, y y: Int = 0) -> GridItem? {
    guard !items.isEmpty, (items.startIndex...items.index(before: items.endIndex)).contains(x)
      else { return nil }

    let row = items[x]

    guard !row.isEmpty, (row.startIndex...row.index(before: row.endIndex)).contains(y)
      else { return nil }

    return row[y]
  }
}

This code is almost the same as the first example, except the subscript now has default arguments. This means that you can use the subscript as follows:

let item = grid[y: 2]

This will automatically call the subscript with a default value of 0 for the x argument.

The fact that it wasn't possible to define default subscript arguments in Swift before was kind of a surprise for me to be honest, but I'm really happy that we have the possibility now. I think it will allow many people to write better subscripts for their objects.

How and when to use callAsFunction in Swift 5.2

A new Swift 5.2 feature is the ability to call instances of types as functions. Or, as the Swift Evolution proposal calls it "Callable values of user-defined nominal types". The very short description of this feature is that it allows you to call instances of any type that has a callAsFunction method implemented as if it's a function:

struct InvestmentsCalculator {
  let input: Double
  let averageGrowthPerYear = 0.07

  func callAsFunction(years: Int) -> Double {
    return (0..<years).reduce(input, { value, _ in
      return value * (1 + averageGrowthPerYear)
    })
  }
}

let calculator = InvestmentsCalculator(input: 1000)
let newValue = calculator(years: 10)

While this is pretty cool, you might wonder when a feature like this is useful in the real world, and how you can apply it in your code. Luckily, that's exactly what I hope to help you discover in today's post, and what I've been trying to find out myself.

Understanding the background of callAsFunction

Swift isn't the only language to allow its users to call instances of certain types as functions. A language that I have used a lot that allows this kind of behavior is Python. The ability to invoke instances as functions is very interesting in certain applications where you want to represent a stateful calculator, parser or computing object. This is very common in complex math operations and machine learning where certain objects might hold on to some state and only implement a single method.

An application that I am more familiar with myself, is to pass a renderer object to a function in Python. I won't bother you with Python code in this post, but I do want to show you what I mean by my previous statement. So let's look at a Swift example:

protocol Route {
  associatedtype Output
}

func registerHandler<R: Route>(_ route: R, _ handler: (R) -> R.Output) {
  return renderer(route)
}

Upon first glance, this shouldn't look too crazy. It's a function that accepts an object that conforms to Route, and a closure that takes this Route as its argument, and returns R.Output, whatever that may be. If you were to call this function, you might write the following:

registerHandler(homeRoute) { route in
  /* Do a bunch of work to create output */

  return output
}

This might work fine for very simple renderers. However, in the context of Python I mentioned before, this kind of code would be running on a web server. And a route would be the equivalent of a URL or path to a webpage. The closure passed to registerHandler is called whenever the associated route is requested by the user. In many cases, a simple closure wouldn't do. The object that ends up handling the route would need to have a database connection, a concept of caching, authenticating and possibly a lot of other functionality. Capturing all of that in a closure just doesn't seem like a great idea. Instead, you'd want some kind of complex object to handle this route. And that's exactly the kind of freedom we get with callAsFunction:

struct HomeHandler {
  let database: Database
  let authenticator: Authenticator

  // etc...

  func callAsFunction<R: Route>(_ route: R) -> R.Output {
    /* Do a bunch of work to create output */

    return output
  }
}

let homeHander = HomeHandler(database: database, authenticator: authenticator)

registerHandler(for: homeRoute, homeHandler)

Patterns like this are quite common in Python, and in my opinion, it's pretty cool to be able to transparently pass entire objects to functions take closures is really neat.

If you know a thing or two about closures, you might be wondering if there's any difference between the previous code, and the following:

registerHandler(for: homeRoute, homeHandler.callAsFunction)

And there really isn't much of a difference other than the amount of typing you need to do. Functions can still be passed to functions that take closures just fine. It just reads a little bit nicer to not have to specify what function on an object needs to be called exactly.

Understanding how to use callAsFunction

Using callAsFunction in Swift is relatively straightforward. Any object that defines a callAsFunction method can be treated as a function. Your callAsFunction can take arguments and return values as shown in the Swift Evolution proposal with the following example:

struct Adder {
  let base: Int

  func callAsFunction(_ x: Int) -> Int {
    return base + x
  }
}

let add3 = Adder(base: 3)
add3(10) // 13

Or it can take no arguments at all:

struct Randomizer<T> {
  let elements: [T]

  func callAsFunction() -> T? {
    return elements.randomElement()
  }
}

let randomizer = Randomizer(elements: [1, 2, 3])
randomizer() // a random element

And you can even have multiple overloads on a single object:

struct Randomizer<T> {
  let elements: [T]

  func callAsFunction() -> T? {
    return elements.randomElement()
  }

  func callAsFunction(resultCount: Int) -> [T] {
    return (0..<resultCount).reduce(into: [T]()) { result, _ in
      if let element = elements.randomElement() {
        result.append(element)
      }
    }
  }
}

let randomizer = Randomizer(elements: [1, 2, 3])
randomizer() // a random element
randomizer(resultCount: 2) // an array with two random elements

The ability to decide whether you want your callAsFunction implementation to take arguments and what its return type is, makes it a pretty powerful feature. You can really customize this feature to your heart's content and because you can add multiple overloads of callAsFunction to your objects, you can use a single object as a function in many contexts.

In Summary

The ability to make your types callable might not be something that you're likely to use a lot. It's a niche feature that has a couple of very specific, yet convenient, applications. In today's post, you saw an example of how Python uses callables in a framework that I've worked with a couple of years ago to build websites. I roughly translated the ability to register route handlers using objects to Swift to give you an idea of what a practical application could look like and give you a little bit of background from a new perspective.

I then proceeded to show you how callAsFunction can be used on your own types. You saw that it's totally up to you to decide whether you want your implementation to take arguments and that you get to pick the return type of callAsFunction yourself. You also learned that it's possible to specify your overloads for callAsFunction which means that you can have multiple flavors of callAsFunction on a single type.

If you have any questions about this post, or if you have feedback for me, don't hesitate to reach out to me on Twitter.