Swift Generics
Writing flexible, reusable code that works with any type
🔄 What are Generics?
Generics enable you to write flexible, reusable functions and types that can work with any type. They avoid duplication and express intent clearly, making your code more maintainable and type-safe.
// Generic function that works with any type
func swapValues(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
var x = 5
var y = 10
swapValues(&x, &y)
print("x: \(x), y: \(y)")
var name1 = "Alice"
var name2 = "Bob"
swapValues(&name1, &name2)
print("name1: \(name1), name2: \(name2)")
Output:
x: 10, y: 5
name1: Bob, name2: Alice
Generic Concepts
Type Parameters
Placeholder names for types using angle brackets
func myFunction(param: T) {
// T is a type parameter
}
Generic Types
Classes, structs, and enums that work with any type
struct Stack {
var items: [Element] = []
}
Constraints
Limit generic types to specific protocols or classes
func compare(a: T, b: T) {
// T must conform to Comparable
}
Associated Types
Define placeholder types within protocols
protocol Container {
associatedtype Item
}
🔹 Generic Functions
Functions that work with multiple types:
// Generic function to find an item in an array
func findIndex(of item: T, in array: [T]) -> Int? {
for (index, value) in array.enumerated() {
if value == item {
return index
}
}
return nil
}
// Works with different types
let numbers = [1, 2, 3, 4, 5]
if let index = findIndex(of: 3, in: numbers) {
print("Found 3 at index \(index)")
}
let names = ["Alice", "Bob", "Charlie"]
if let index = findIndex(of: "Bob", in: names) {
print("Found Bob at index \(index)")
}
// Generic function with multiple type parameters
func combine(first: T, second: U) -> String {
return "\(first) + \(second)"
}
let result = combine(first: 42, second: "hello")
print(result)
Output:
Found 3 at index 2
Found Bob at index 1
42 + hello
🔹 Generic Types
Create reusable data structures with generics:
// Generic Stack implementation
struct Stack {
private var items: [Element] = []
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element? {
return items.popLast()
}
func peek() -> Element? {
return items.last
}
var isEmpty: Bool {
return items.isEmpty
}
var count: Int {
return items.count
}
}
// Using the generic stack with different types
var intStack = Stack()
intStack.push(1)
intStack.push(2)
intStack.push(3)
print("Int stack top: \(intStack.peek() ?? 0)")
var stringStack = Stack()
stringStack.push("Hello")
stringStack.push("World")
print("String stack top: \(stringStack.peek() ?? "")")
print("Popped: \(stringStack.pop() ?? "")")
Output:
Int stack top: 3
String stack top: World
Popped: World