Swift Tips: Closure-Based CollectionType APIs

Blake Merryman
BPXL Craft
Published in
3 min readAug 14, 2017

--

We deal with collections of data every day, whether they are dictionaries, arrays, or sets. Working with collections leads to a lot of boilerplate: for-in statements, temporary storage variables, and many lines of code.

Swift introduces a new set of functional, closure-based APIs for dealing with collections. These methods cut down on boilerplate code, help to highlight the important information, and improve readability. The methods are also chainable, so you can easily compose advanced logic.

Core Methods

forEach(_:)

This is a simple iterator that calls a closure on each element of self in the same order as for-in.

[“a”, “b”, “c”, …, “z”].forEach { element in
print(“\(element), “)
}
// Prints: a, b, c, …, z,

map(transform:)

This returns an Array containing the results of mapping a transform over self.

let upperCaseLetters = [“a”, “b”, “c”, …, “z”].map { element in
return element.uppercase
}
// upperCaseLetters is [“A”,”B”,”C”,…,”Z”]

filter(predicate:)

This returns an Array containing the ordered elements of self that satisfy the supplied predicate.

let vowels = [“a”, “b”, “c”, …, “z”].filter { element in
return element == “a”
|| element == “e”
|| element == “i”
|| element == “o”
|| element == “u”
}
// vowels is [“a”,”e”,”i”,”o”,”u”]

reduce(initial:,combine:)

This returns the result of repeatedly calling combine with an accumulated value initialized to initial and each element of self.

let alphabetString = [“a”, “b”, “c”, …, “z”]. reduce(“”) {
combinedElements, element in
return combinedElements.appending(element)
}
// alphabetString is "abc...z"

Bonus Method

flatMap(transform:)

This returns an Array containing the results of mapping transform over self and flattening the result.

Warning: flatMap(_:) can have unexpected side effects if you aren’t careful. For example:

let a = [[“a”, “b”], nil, [“c”, “d”], [“e”, “f”, “g”]]
print(a.flatMap { return $0 })
// [[“a”, “b”], [“c”, “d”], [“e”, “f”, “g”]]
let b = [[“a”, “b”], [“c”, “d”], [“e”, “f”, “g”]]
print(b.flatMap { return $0 })
// [“a”, “b”, “c”, “d”, “e”, “f”, “g”]

In the first example, array a has a nil element. When flatMap is called, the array is “flattened” to only include non-nil elements. In the second example, array b only has non-nil elements. When flatMap is called, the subarrays are flattened into a single array.

Here are a couple of notes on using collection types:

  • These are not silver bullets. You will occasionally still have to use for-in. For example, within the closures used in these methods, you cannot affect the scope outside by calling break, continue, return, etc.
  • You can also pass in functions that share type signature with the closures. This is a great way to store complex functionality elsewhere and keep the logic simple, clean, and readable inline.

Pro Tip: These collection APIs also serve as an excellent reference for how to build a generic, protocol-oriented API.

The closure-based APIs covered here can really help with the readability and maintainability of your codebase by removing a ton of boilerplate and bringing the intent of your code forward.

Come back next week for a detailed look at how to configure properties inline with closures.

For more insights on design and development, subscribe to BPXL Craft and follow Black Pixel on Twitter.

Black Pixel is a creative digital products agency. Learn more at blackpixel.com.

--

--