Advanced Swift Feature: Generics

A portrait painting style image of a pirate holding an iPhone.

by The Captain

on
April 15, 2023

Advanced Swift Feature: Generics

Generics are an advanced feature in Swift that allow developers to write flexible and reusable code. They provide a way to write functions, structures, and classes that work with any type without knowing beforehand what that type is.

Generic Functions

A generic function is a function that can work with any type. You can declare a generic function using the <T> syntax to define a placeholder type that will be replaced by the actual type when the function is called.

func swapTwoValues(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}

In this example, the function swapTwoValues() takes two arguments a and b, both of type T, which is a placeholder for any type. The &inout keyword means that the arguments are passed by reference, which allows the function to modify their values. The function then creates a temporary variable to hold the value of a, assigns b to a, and finally assigns the temporary variable to b. This effectively swaps the values of a and b.

Generic Types

Similar to generic functions, you can also create generic types in Swift, such as generic structures and classes. These types can work with any type, just like generic functions.

struct Stack {
    var items = [T]()

    mutating func push(_ item: T) {
        items.append(item)
    }

    mutating func pop() -> T? {
        return items.isEmpty ? nil : items.removeLast()
    }

    func peek() -> T? {
        return items.last
    }

    var count: Int {
        return items.count
    }
}

In this example, we create a generic structure Stack that can work with any type. The structure has an items array property of type T, a mutating push() function that adds an item to the stack, a mutating pop() function that removes the last item from the stack, a peek() function that returns the top item from the stack without removing it, and a count property that returns the number of items in the stack.

Type Constraints

While you can use any type with generic functions and types, sometimes you may want to constrain the types that can be used. Swift provides the option to place constraints on generic types and functions, using the where clause.

func allEqual(_ array: [T]) -> Bool {
    guard let first = array.first else {
        return true
    }
    for element in array {
        if element != first {
            return false
        }
    }
    return true
}

In this example, the function allEqual() takes an array of values of type T, which we constrain to conform to the Equatable protocol using T: Equatable. The function then uses the first property of the array to get the first element, and iterates over the remaining elements of the array, checking if they are equal to the first element using the != operator. If any element is not equal to the first element, the function returns false. Otherwise, it returns true.

Conclusion

Generics are a powerful feature of Swift that enable developers to write flexible and reusable code. They allow functions, structures, and classes to work with any type, and provide the option to constrain the types that can be used. By leveraging the power of generics, you can write code that can be used with a wide variety of types and avoid duplicating code.