Blog

Latest Post

Swift Tip: In-Place Map

When we find ourselves repeating code on both the left-hand and right-hand side of an assignment operator, it is often a hint that we could be using a mutating method instead (for example, we could use toggle on Bool).

Consider the following example:

struct User {
    var favorites: [String]
}

var users = [
    "ida": User(favorites: ["https://www.objc.io"])
]

Now let’s say we want to change the favorites for ida: we’d like to replace each “https” with “http”. It’s not uncommon to see code like this:

// Check if the key is present
if var u = users["ida"] {
    // Modify a nested property
    u.favorites = u.favorites.map {
        $0.replacingOccurrences(of: "https", with: "http")
    }
    // Re-assign the new value
    users["ida"] = u
}

There are a few things that are tricky in the code above: we need to make sure that the key matches in both the lookup and the assignment, and the repetition of favorites in the assignment is also not so nice. Ideally, we’d write this in a single line:

users["ida"]?.favorites = (users["ida"].favorites ?? []).map { /* ... */ }

This still has duplication on either side of the assignment operator, and the nil coalescing is distracting from what we’re trying to say. Instead, we could define a version of map that mutates the array in place:

extension Array {
    mutating func mapInPlace(_ transform: (Element) -> Element) {
        self = map(transform)
    }
}

Now we can write our code without duplication:

users["ida"]?.favorites.mapInPlace {
    $0.replacingOccurrences(of: "https", with: "http")
}

If you like this approach, you can make it available to many more types by writing mapInPlace on MutableCollection. If you like mutation, the transform could be written in inout-style as well.

In Swift Talk 21 (a public episode), we look at other ways to work with structs. Our book, Advanced Swift, covers mutating, inout, and value types in depth.

Subscribers make our public episodes possible.

To support us, you can subscribe too.

Previous Posts

Swift Tip: Local Computed Variables

Sometimes we want to compute the same expression twice in a function. For example, here’s a simplified version of some XML parsing code we recently wrote:

func parseElement(name: String, text: String) {
    if name == "trk" {
        let t = text.trimmingCharacters(in: .whitespaces)
        // process element
    } else if name == "ele" {
        let elevationText = text.trimmingCharacters(in: .whitespaces)
        guard let elevation = Int(elevationText) else { return }
    } else {
        // no need to trim text
    }
}

Note that we’ve duplicated the text.trimmingCharacters(in: .whitespaces) in two branches. Of course, we can easily pull it out into a variable outside of the if statement:

func parseElement(name: String, text: String) {
    let trimmed = text.trimmingCharacters(in: .whitespaces)
    if name == "trk" {
        let t = trimmed
        // process element
    } else if name == "ele" {
        guard let elevation = Int(trimmed) else { return }
    } else {
        // no need to trim text
    }
}

This works fine, except that it can really slow down our parsing code: we’re trimming the text for every element that we parse, but we only really need it in case of trk and ele elements. If trimmed were a struct or class property, we could write lazy in front of it, but alas, that doesn’t work for a local variable.

Instead, we can make trimmed a local computed property, like so:

func parseElement(name: String, text: String) {
    var trimmed: String { text.trimmingCharacters(in: .whitespaces) }
    if name == "trk" {
        let t = trimmed
        // process element
    } else if name == "ele" {
        guard let elevation = Int(trimmed) else { return }
    } else {
        // no need to trim text
    }
}

This will compute trimmed only when you need it. Of course, this solution has its drawbacks, too: if you access the trimmed computed property more than once, it also computes the value more than once. You have to be careful to only use it when you need it, and if you do need it multiple times, cache the value.

The technique above can also work well for complicated loop conditions. For example, here’s a completely made-up while loop with three conditions:

while !done && queue.count > 1 && queue[1] != "stop" {
    // process the queue
}

If we want to group the two queue-related conditions into one, a local computed property can be used:

var canContinue: Bool { return queue.count > 1 && queue[1] != "stop" }
while !done && canContinue {
    // process the queue
}

For more advanced tricks with local properties, watch Swift Talk 61 and Swift Talk 63, where we use Swift 4’s KeyPaths to build a new hybrid type, combining useful features of both classes and structs — a fun way to push the limits of the language!

Thanks to our Subscribers, both these episodes are public. 🙇‍♂️

If you’d like to support us, you can subscribe too.

Swift Tip: Capture Lists

Since Swift is a reference-counted language, we have to be careful not to create reference cycles — especially when we’re capturing self within a closure. Consider this code snippet, adapted from our new book, App Architecture:

final class PlayViewController: UIViewController {
    let viewModel = PlayViewModel()
    var observation: NSKeyValueObservation?

    func viewDidLoad() {
        super.viewDidLoad()
        // ...
        observation = viewModel.observe(\.navigationTitle, options: [.initial, .new]) { [unowned self] _, change in
            guard let v = change.newValue else { return }
            self.navigationItem.title = v
        }
    }
    // ...
}

Here we use the [unowned self] capture list to avoid a reference cycle. We could also have captured self weakly, but since we can guarantee that the lifetime of the observation is coupled to the lifetime of the view controller, we can get away with using unowned.

A few weeks ago, co-author Matt Gallagher pointed out an interesting alternative that we hadn’t considered before: instead of capturing an unowned reference to self, we can also use the capture list to capture a strong reference to the navigation item directly:

observation = viewModel.observe(\.navigationTitle, options: [.initial, .new]) { [navigationItem] _, change in
    guard let v = change.newValue else { return }
    navigationItem.title = v
}

This way the closure doesn’t capture self at all, but only the reference to the navigation item it actually needs. The benefit is that we can avoid the somewhat “dangerous” unowned reference to self while also not having to deal with a weak optional self within the closure.

Finally, we can even assign a different name to the captured reference if we want to avoid shadowing the name of the navigationItem property on the view controller:

observation = viewModel.observe(\.navigationTitle, options: [.initial, .new]) { [navItem = navigationItem] _, change in
    guard let v = change.newValue else { return }
    navItem.title = v
}

The lesson here is to consider carefully what you actually need to capture in your closure. Do you really need a reference to self? Or do you just need a reference to an object on self? If you can capture something more specific, you can often avoid dealing with weak or unowned references.

To learn more, our latest books deal with advanced concepts in Swift.

Swift Tip: Unwrapping Optionals

When we use the ! operator to force-unwrap an optional value in Swift Talk, we sometimes hear from viewers that it would be better not to use force-unwrapping. However, while we try to be very careful about force-unwrapping an optional value, it’s mostly a very deliberate choice if we do force-unwrap. The key questions we ask ourselves in these cases is: does the nil value occur due to a programmer error?

Consider the example of creating an URL from a static string like this:

let url: URL? = URL(string: "https://www.objc.io")

The URL initializer is failable, because not every string represents a valid URL:

let url: URL? = URL(string: "🤔")
assert(url == nil)

However, if we create a URL from a static string, it’s the programmer’s responsibility to specify a valid URL. A string that results in a nil value is like a typo or a logic error in your code. Therefore we force-unwrap the result to crash immediately in case we’ve made a mistake:

let url: URL = URL(string: "https://www.objc.io")!

If you use force-unwrapping like this carefully, you can read the ! character in your code as an assertion. It signifies a check for the correctness of your code.

We encountered a similar, though less obvious example when writing a concurrent map function in Swift Talk 90:

extension Array {
    func concurrentMap<B>(_ transform: @escaping (Element) -> B) -> [B] {
        var result = Array<B?>(repeating: nil, count: count)
        let q = DispatchQueue(label: "sync queue")
        DispatchQueue.concurrentPerform(iterations: count) { idx in
            let element = self[idx]
            let transformed = transform(element)
            q.sync {
                result[idx] = transformed
            }
        }
        return result.map { $0! }
    }
}

At the very end of the function, we map over the result array and force unwrap all the values. We could have avoided the force-unwrap by using flatMap instead of map, but this would only serve to mask potential programming errors. The result array must contain the same number of elements as the original array, i.e. there must not be a nil value in it. If there is, we have made a mistake and should crash immediately.

We talk much more about handling optionals in Swift Talk 93: besides force-unwrapping, we look at API design with optionals, optional chaining, implicitly-unwrapped optionals and more.

Subscribe to access all our episodes.

Swift Tip: Lazy Infinite Sequences

Sometimes it can be more natural to write your code in a functional way. For example, let’s say you want to find the first square number larger than 100. An imperative program is easily written:

var current = 1
while current*current <= 100 {
    current += 1
}
result = current*current

In Swift, we also have the option to write this in a more functional way:

let result = (1...)
   .lazy
   .map { $0 * $0 }
   .first(where: { $0 > 100})

The second snippet starts with all possible integers, maps over it to find the squares, and then takes the first element. The lazy turns it into a lazy collection, and makes sure the map step is only evaluated for an element when it’s really needed. Finally, the first(where:) repeatedly asks the lazy collection for the next element (evaluating the map) until it finds an element for which the condition is true.

The functional version is also often easier to change. For example, let’s say we want the first ten squares which are larger than 100. The imperative code changes quite drastically:

var current = 1
var results: [Int] = []
while results.count < 10 {
    if current*current > 100 {
        results.append(current*current)
    }
    current += 1
}

Whereas the functional code is much more similar to the previous version:

let results = (1...)
   .lazy
   .map { $0 * $0 }
   .filter { $0 > 100 }
Array(results.prefix(10))

Note that the results variable in the functional version is still lazy: nothing is computed until we turn it into an Array.

One major advantage of lazy sequences is that the consumer of the sequence has control over how many elements should be iterated over. In the imperative version this needs to be built into the producer, leading to less flexible code.

If you’d like to know more about similar techniques, our Advanced Swift book is a great place to start. Prefer video? The Functional Programming Swift Talk Collection is one of our largest.

If you’d like to watch all our episodes, you can subscribe 😉