← Back to archive

Development · Swift

Reusable code with Swift generics

Reduce duplication with generic functions, generic types, and type constraints while preserving type safety.

Read the Korean original on Velog ↗English translation

Development archive cover

When several functions do the same work for different types, duplication quickly becomes more noticeable than the implementation itself. Generics let us write the shared behavior once and supply the concrete type later.

Generic functions

A type parameter such as <T> lets a function work with different types without giving up type safety.

var a = 2
var b = 3
var apple = "apple"
var banana = "banana"

func swapElements<T>(_ first: inout T, _ second: inout T) {
    let temp = first
    first = second
    second = temp
}

swapElements(&a, &b) // Int values
swapElements(&apple, &banana) // String values

Separate type parameters with commas when a function needs more than one.

func someFunction<One, Two>(_ first: One, _ second: Two) { ... }

Generic types

Structures, classes, and enumerations can have type parameters too.

struct Stack<Element> {
    var items = [Element]()
    
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
}

When creating an instance, provide the concrete type, either explicitly or through type inference where possible.

let stack1: Stack<Int> = .init() // An instance with Int elements
let stack2 = Stack<String>.init() // An instance with String elements

Extending generic types

An extension can use the generic type's existing type parameters. It does not redeclare them.

extension Array {
    mutating func pop() -> Element {
        return self.removeLast()
    }
}

Type constraints

Constraints can require a type parameter to inherit from a particular class or conform to a protocol.

func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) { ... }
← Back to archive