Generics in Swift: Why They Are Needed and How to Use Them

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: What are Generics and why are they important in Swift



Generics are one of the most powerful and elegant features of the Swift language. They allow you to write flexible, reusable code that works with any data type while maintaining strong typing. If you've ever used Array<Int>, Dictionary<String, Any>, or Optional<T> — you've already encountered Generics.



In this article, we'll explore why Generics are needed in Swift, how they work in practice, and show examples for iOS/macOS development. The material will be useful for both beginners and experienced developers looking to structure their knowledge.



1. Why are Generics needed in Swift?



The main purpose of Generics is to avoid code duplication and improve safety. Imagine you need to write a function that swaps two values. Without Generics, you would have to create separate functions for Int, String, Double, and so on:



func swapInts(_ a: inout Int, _ b: inout Int) {    let temp = a    a = b    b = temp}

func swapStrings(_ a: inout String, _ b: inout String) { let temp = a a = b b = temp}


With Generics, this task is solved with one universal function:



func swapValues<T>(_ a: inout T, _ b: inout T) {    let temp = a    a = b    b = temp}

var x = 10, y = 20swapValues(&x, &y) // x = 20, y = 10

var name1 = "Alice", name2 = "Bob"swapValues(&name1, &name2) // name1 = "Bob", name2 = "Alice"


Advantages of Generics:

  • Code Reusability: one function/type works with any data type.
  • Type Safety: the compiler checks types at compile time, eliminating runtime errors.
  • Performance: Generics in Swift are implemented through compile-time polymorphism (specialization), so there is no overhead from type casting.
  • Readability: code becomes more abstract and concise.


2. Generic Functions: syntax and examples



A generic function is declared using angle brackets <T>, where T is a placeholder for the type. You can use any identifier: T, Element, Value, etc.



Basic example: finding the index of an element



func findIndex<T: Equatable>(of value: T, in array: [T]) -> Int? {    for (index, item) in array.enumerated() {        if item == value {            return index        }    }    return nil}

let numbers = [1, 3, 5, 7, 9]if let index = findIndex(of: 5, in: numbers) { print("Index of number 5: \\(index)") // Index of number 5: 2}


Notice the constraint <T: Equatable>. This is a type constraint. It tells the compiler: "T must conform to the Equatable protocol". Without this constraint, the == operator would not be available.



Multiple type parameters



You can use multiple type parameters:



func combine<T, U>(_ first: T, with second: U) -> String {    return "\\(first) + \\(second)"}

let result = combine(42, with: "answer")print(result) // 42 + answer


3. Generic Types: creating generic structures and classes



Generics apply not only to functions but also to types. The most popular example is Array<Element>. Let's create our own generic structure — a stack (LIFO):



struct Stack<Element> {    private var items: [Element] = []        mutating func push(_ item: Element) {        items.append(item                

Blogs

Book Recommendations