๐ŸŽ“ A1Learner
๐Ÿ”
0 of 27 lessons completed

Swift Associated Types

An associated type is a placeholder type inside a protocol. Each conforming type fills the placeholder with a concrete type of its own.

One conformer fixes Item

The Container protocol requires an items array of its associated Item type. NameBox meets that requirement with an array of strings, so its Item is String.

protocol Container {
    associatedtype Item
    var items: [Item] { get }
}
struct NameBox: Container {
    var items: [String]
}
func itemCount<C: Container>(_ box: C) -> Int {
    return box.items.count
}
let names = NameBox(items: ["Ana", "Ben", "Cleo"])
print(itemCount(names))
print(names.items.first ?? "none")

Two conformers, two Item types

NameBox fixes Item to String and ScoreBox fixes Item to Int. The firstItem function returns the associated type of the box it receives, spelled through its generic parameter.

protocol Container {
    associatedtype Item
    var items: [Item] { get }
}
struct NameBox: Container {
    var items: [String]
}
struct ScoreBox: Container {
    var items: [Int]
}
func firstItem<C: Container>(_ box: C) -> C.Item? {
    return box.items.first
}
let names = NameBox(items: ["Ana", "Ben"])
let scores = ScoreBox(items: [90, 75])
if let firstName = firstItem(names) {
    print(firstName)
}
if let firstScore = firstItem(scores) {
    print(firstScore)
}

A fallback of the associated type

The firstOr function takes a fallback value typed as the associated type of its box. The empty box yields the fallback, and the full box yields its first stored name.

protocol Container {
    associatedtype Item
    var items: [Item] { get }
}
struct NameBox: Container {
    var items: [String]
}
func firstOr<C: Container>(_ box: C, _ fallback: C.Item) -> C.Item {
    if let first = box.items.first {
        return first
    }
    return fallback
}
let empty = NameBox(items: [])
let names = NameBox(items: ["Ana", "Ben"])
print(firstOr(empty, "Guest"))
print(firstOr(names, "Guest"))

Example

protocol Container {
    associatedtype Item
    var items: [Item] { get }
}
struct WordBox: Container {
    var items: [String]
}
struct NumberBox: Container {
    var items: [Int]
}
func printItems<C: Container>(_ box: C) {
    for item in box.items {
        print(item)
    }
}
let words = WordBox(items: ["Red", "Blue"])
let numbers = NumberBox(items: [7, 9])
printItems(words)
printItems(numbers)
print(words.items.count + numbers.items.count)

Key points

  • The associatedtype keyword declares the placeholder inside a protocol.
  • A protocol can use its associated type in its own requirements.
  • A conforming type fixes the associated type through the matching requirement.
  • Different conformers can fix the same associated type to different concrete types.
  • A generic function can name the associated type through its type parameter, as in a return type of the parameter dot Item.
  • An inline constraint ties a generic parameter to the protocol in angle brackets.

In this example, WordBox fixes the associated Item type to String and NumberBox fixes it to Int. The printItems function accepts a box through an inline constraint and prints its stored values. One call prints the two words, the next call prints the two numbers, and the final line adds the two counts.

Note

The associated type is set by the conformer, never picked by the caller.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

A protocol declares associatedtype Item, and a conforming struct stores its items as [Int]. What is Item for that struct?