Imagine writing a function that swaps two Ints, then one for two Strings, then one for two Doubles. The code is identical each time; only the type changes. Generics let you write that code once, with a placeholder type that Swift fills in at each call.
You've been using generics since your first Swift lesson. Array<Element> is a generic struct, so [Int] and [String] are the same code working with different element types. Dictionary<Key, Value>, Optional<Wrapped>, Set<Element> and Result<Success, Failure> are all generic too. That's why one sorted() method works on arrays of numbers, strings and dates.
Generics keep full type safety: a Stack<Int> only ever holds Ints, and the compiler checks it. In this lesson you'll write generic functions and types, constrain them so they can compare or hash their values, use protocols with associated types, and understand the some keyword you see in SwiftUI.
Generic functions
Put a type parameter in angle brackets after the function name:
func firstItem<T>(of items: [T]) -> T? {
items.isEmpty ? nil : items[0]
}
firstItem(of: [3, 1, 2]) // Optional(3), T is Int
firstItem(of: ["a", "b"]) // Optional("a"), T is StringTis a placeholder; Swift infers the real type from the arguments.- Every use of
Tin one call refers to the same type, so you can't mix an Int array with a String result. - Use descriptive names when they help:
Element,Key,Value.
Generic types
Structs, classes and enums can be generic too:
struct Stack<Element> {
private var items: [Element] = []
mutating func push(_ item: Element) { items.append(item) }
mutating func pop() -> Element? { items.popLast() }
var top: Element? { items.last }
}
var history = Stack<String>()
history.push("Home")Once created, history is a Stack<String>, and pushing an Int is a compile error.
Constraints and where clauses
A plain T can do very little, because Swift knows nothing about it. Constraints require the type to conform to a protocol, which unlocks that protocol's abilities:
func largest<T: Comparable>(_ items: [T]) -> T? {
items.max()
}
func count<T: Equatable>(_ value: T, in items: [T]) -> Int {
items.filter { $0 == value }.count
}For more complex rules, use a where clause: func merge<C: Collection>(_ c: C) where C.Element: Hashable. Constraints are also how generic extensions work, as you saw with extension Array where Element == Int.
Associated types and some
A protocol can have an associated type, a placeholder filled in by each conforming type:
protocol Container {
associatedtype Item
var items: [Item] { get }
}Collection uses this for Element, and SwiftUI's View uses it for Body. Returning some View means "a specific type that conforms to View, which the compiler knows but the caller doesn't need to write out". Without some, a SwiftUI body's real type would be a long nested generic type that's impractical to type.
Generics you already use
You've been using generic types since the arrays lesson. The short forms are just friendlier spellings:
| Short form | Full generic name |
|---|---|
[Int] | Array<Int> |
[String: Int] | Dictionary<String, Int> |
String? | Optional<String> |
Set<Int> | Set<Int> (no short form) |
That's why one sorted() or count works for every kind of array: they're written once, for Array<Element>, and used with any element type.
A function or type can have more than one placeholder, separated by commas. Each one can be a different type:
func makePair<A, B>(_ first: A, _ second: B) -> (A, B) {
(first, second)
}
let entry = makePair("Ada", 36) // (String, Int)
struct Pair<First, Second> {
let first: First
let second: Second
}You can also extend a generic type only for certain placeholders. This adds sum to stacks of numbers, but not to stacks of strings:
extension Stack where Element: Numeric {
var sum: Element { items.reduce(0, +) }
}Swift Generics Syntax
Generic syntax
func swapValues<T>(_ a: inout T, _ b: inout T) { (a, b) = (b, a) }
func maxOf<T: Comparable>(_ x: T, _ y: T) -> T { x > y ? x : y }
struct Box<Value> { var value: Value }
let b = Box(value: 3) // Box<Int>
func allEqual<C: Collection>(_ c: C) -> Bool where C.Element: Equatable {
c.allSatisfy { $0 == c.first }
}
protocol Store { associatedtype Item; func all() -> [Item] }
var body: some View { Text("Hi") }Swift Generics Summary
- Generics use type parameters such as
<T>to write code once for many types. - Swift infers the type parameter from arguments and keeps everything type-safe.
- Array, Dictionary, Optional, Set and Result are generic types.
- Constraints such as
T: Comparableand where clauses unlock operations on T. - Protocols use associatedtype;
some Protocolreturns a hidden concrete type.
Swift Generics Examples
Each example shows the code, the output it produces and what each part does.
A generic Stack
struct Stack<Element> {
private var items: [Element] = []
var isEmpty: Bool { items.isEmpty }
var top: Element? { items.last }
mutating func push(_ item: Element) { items.append(item) }
mutating func pop() -> Element? { items.popLast() }
}
var screens = Stack<String>()
screens.push("Home")
screens.push("Products")
screens.push("Checkout")
print(screens.pop() ?? "none", screens.top ?? "none")
var undo = Stack<Int>()
undo.push(10)
print(undo.pop()!, undo.isEmpty)Checkout Products
10 trueLine-by-line explanation
- Line 1Element is a placeholder filled in when a Stack is created.
- Lines 7 to 8Methods use Element, so they accept and return the right type for each stack.
- Line 11This stack can only ever hold Strings.
- Line 17The same code works with Ints.
Constrained generic functions
func largest<T: Comparable>(_ items: [T]) -> T? {
guard var best = items.first else { return nil }
for item in items where item > best {
best = item
}
return best
}
func occurrences<T: Equatable>(of value: T, in items: [T]) -> Int {
items.filter { $0 == value }.count
}
func unique<T: Hashable>(_ items: [T]) -> [T] {
var seen = Set<T>()
return items.filter { seen.insert($0).inserted }
}
print(largest([4, 19, 7]) ?? 0, largest(["pear", "apple"]) ?? "")
print(occurrences(of: "red", in: ["red", "blue", "red"]))
print(unique([3, 1, 3, 2, 1]))19 pear
2
[3, 1, 2]Line-by-line explanation
- Line 1Comparable lets the body use >.
- Line 9Equatable lets the body use ==.
- Lines 13 to 15Hashable lets values go into a Set; insert reports whether the value was new, which keeps the first of each.
- Line 18The same function works for Ints and Strings.
A protocol with an associated type
protocol Repository {
associatedtype Item
func all() -> [Item]
}
struct Book { let title: String }
struct BookRepository: Repository {
func all() -> [Book] {
[Book(title: "Dune"), Book(title: "Emma")]
}
}
struct NumberRepository: Repository {
func all() -> [Int] { [1, 2, 3] }
}
func count<R: Repository>(in repo: R) -> Int {
repo.all().count
}
print(BookRepository().all().map(\.title))
print(count(in: BookRepository()), count(in: NumberRepository()))["Dune", "Emma"]
2 3Line-by-line explanation
- Line 2Each conforming type decides what Item is.
- Line 9Swift infers that Item is Book from the return type.
- Line 18A generic function can work with any Repository, whatever its Item.
- Line 22\.title is a key path, a short way to read one property from each item.
Common Swift Generics Mistakes
Using an operator without a constraint
func biggest<T>(_ a: T, _ b: T) -> T {
a > b ? a : b // error: binary operator '>' cannot be applied to two 'T' operands
}func biggest<T: Comparable>(_ a: T, _ b: T) -> T {
a > b ? a : b
}Mixing types for one placeholder
func pair<T>(_ a: T, _ b: T) -> [T] { [a, b] }
pair(1, "one") // error: T can't be both Int and Stringfunc pair<A, B>(_ a: A, _ b: B) -> (A, B) { (a, b) }
pair(1, "one")Generics where a concrete type would do
func formatPrice<T: BinaryFloatingPoint>(_ value: T) -> String { ... } // only ever used with Doublefunc formatPrice(_ value: Double) -> String { ... }Swift Generics in Real Projects
Generics in iOS code
Networking layers often have one generic function such as func fetch<T: Decodable>(_ url: URL) async throws -> T, which downloads and decodes any model type. SwiftUI's ForEach, List and Binding are generic, and so is every view that returns some View. Reusable components, like a generic PagedList<Item> or a cache Cache<Key, Value>, rely on generics to work with any data.
Swift Generics FAQ
What does T mean in Swift?
T is a conventional name for a generic type placeholder, short for "type". In func first<T>(of items: [T]) -> T?, T becomes Int, String or any other type each time the function is called. You can use any name, and descriptive ones like Element often read better.
What is the difference between generics and Any in Swift?
A generic keeps the real type: firstItem(of: [1, 2]) returns an Int?, checked by the compiler. Any throws the type information away, so you have to cast values back with as? and risk mistakes at run time. Prefer generics whenever the type is consistent within a call.
Why does Swift say "Binary operator cannot be applied to two T operands"?
A plain generic T could be any type, including ones without ==, < or +. Add a constraint that provides the operator, such as T: Equatable for ==, T: Comparable for <, or T: Numeric for + and *.
What is an associatedtype in a Swift protocol?
It's a placeholder type inside a protocol that each conforming type fills in. Collection has an associated Element, so an array of strings has String as its element. It lets one protocol describe containers or values of many different types.
Are generics slow in Swift?
No. The compiler can create specialised versions of generic code for the types you actually use, especially within the same module, so generic code usually runs as fast as hand-written code for each type. Choose generics for clarity and reuse without worrying about speed.
Swift Generics Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Complete the declaration so the function works with any type T.
func wrap(_ value: T) -> [T] {
[value]
}
What does this print?
struct Box<Value> { var value: Value }
let b = Box(value: "hi")
print(b.value.count)
Why does func maxOf<T>(_ a: T, _ b: T) -> T { a > b ? a : b } fail to compile?
What does associatedtype declare?
What does some View as a return type mean?
Swift Generics Quiz
5 questions to check what you learned.
Which of these is a generic type?
- Array
- Int
- String
- Bool
How does Swift usually know what T is in a generic call?
- You always write it
- It's always Any
- It picks Int
- It infers it from the arguments
Which constraint lets a generic type be stored in a Set?
- Comparable
- Hashable
- Codable
- Identifiable
What's a where clause used for on a generic function?
- Loops
- Filtering arrays
- Adding extra requirements, such as C.Element: Equatable
- Error handling
Can a Stack<String> accept an Int?
- Yes
- Only with a cast
- Only at run time
- No, generics keep full type safety