SWIFT
Swift Generics: Syntax, Usage, and Examples
Swift generics let you write flexible and reusable code by allowing types to be specified later. Instead of writing separate implementations for different data types, generics let you define a single function, class, struct, or protocol that works with any type.
How to Use Generics in Swift
To define a generic function or type, use angle brackets (<T>
), where T
represents a placeholder type.
Generic Function
func swapValues<T>(a: inout T, b: inout T) {
let temp = a
a = b
b = temp
}
var x = 5
var y = 10
swapValues(a: &x, b: &y)
print(x, y) // Output: 10 5
The T
placeholder lets this function work with any type, whether it's an integer, string, or custom struct.
Generic Class
You can create a generic class to store different types while keeping your code reusable.
class Box<T> {
var value: T
init(value: T) {
self.value = value
}
}
let intBox = Box(value: 42)
let stringBox = Box(value: "Hello")
print(intBox.value) // Output: 42
print(stringBox.value) // Output: Hello
Generic Struct
Similar to a class, a generic struct can hold any type.
struct Pair<T, U> {
let first: T
let second: U
}
let pair = Pair(first: "Alice", second: 30)
print(pair.first) // Output: Alice
print(pair.second) // Output: 30
Generic Enum
You can use generics in enums, which is especially useful for type-safe APIs.
enum Result<T> {
case success(T)
case failure(String)
}
let successResult = Result.success(100)
let failureResult = Result.failure("Something went wrong")
When to Use Generics in Swift
Avoiding Code Duplication
Generics let you write a single, reusable function instead of separate versions for different types.
func printValue<T>(_ value: T) {
print("Value:", value)
}
printValue(10) // Output: Value: 10
printValue("Swift") // Output: Value: Swift
Working with Collections
Swift generics make it possible to create flexible collection types.
struct Stack<T> {
private var elements: [T] = []
mutating func push(_ element: T) {
elements.append(element)
}
mutating func pop() -> T? {
return elements.popLast()
}
}
var intStack = Stack<Int>()
intStack.push(5)
intStack.push(10)
print(intStack.pop()!) // Output: 10
Protocols with Associated Types
Swift generics work with protocols using associated types to allow flexibility in implementation.
protocol Container {
associatedtype Item
mutating func add(_ item: Item)
func count() -> Int
}
struct Bag<T>: Container {
private var items: [T] = []
mutating func add(_ item: T) {
items.append(item)
}
func count() -> Int {
return items.count
}
}
var bag = Bag<String>()
bag.add("Apple")
bag.add("Banana")
print(bag.count()) // Output: 2
Examples of Generics in Swift
Generic Function with Constraints
You can constrain generics to specific types using where
.
func findLargest<T: Comparable>(in array: [T]) -> T? {
return array.max()
}
let numbers = [3, 7, 2, 9]
print(findLargest(in: numbers)!) // Output: 9
Here, T: Comparable
ensures that only types that conform to the Comparable
protocol can be used.
Using Generics in Protocols
A Swift generic protocol allows flexibility while enforcing specific behavior.
protocol Identifiable {
associatedtype ID
var id: ID { get }
}
struct User: Identifiable {
let id: Int
}
let user = User(id: 101)
print(user.id) // Output: 101
Generic Type Alias
A generic type alias provides a shorthand for commonly used generic types.
typealias StringDictionary<T> = Dictionary<String, T>
let userAges: StringDictionary<Int> = ["Alice": 30, "Bob": 25]
print(userAges["Alice"]!) // Output: 30
Learn More About Generics in Swift
Generic Parameters vs. Associated Types
- A generic parameter (
<T>
) is defined in functions, structs, and classes. - An associated type (
associatedtype
) is used in protocols to allow type flexibility.
Generic Constraints
You can use where
to constrain a generic to certain types or protocols.
func printIfEqual<T: Equatable>(_ a: T, _ b: T) {
if a == b {
print("Equal")
} else {
print("Not equal")
}
}
printIfEqual(5, 5) // Output: Equal
printIfEqual("Hi", "Hello") // Output: Not equal
Generic Functions with Multiple Parameters
You can use multiple generic types to handle different kinds of data.
func combine<A, B>(_ a: A, _ b: B) -> String {
return "\(a) and \(b)"
}
print(combine(5, "Apples")) // Output: 5 and Apples
Extending Generic Types
You can extend generic types to add extra functionality.
extension Stack {
func peek() -> T? {
return elements.last
}
}
var numberStack = Stack<Int>()
numberStack.push(20)
print(numberStack.peek()!) // Output: 20
Optional Generics
A Swift optional generic lets you work with optional types in generics.
func unwrap<T>(_ value: T?) -> T {
return value ?? fatalError("Unexpected nil value")
}
let number: Int? = 42
print(unwrap(number)) // Output: 42
Generic Properties
A generic property allows you to define a property with any type.
struct Wrapper<T> {
var value: T
}
let wrappedInt = Wrapper(value: 99)
let wrappedString = Wrapper(value: "Swift")
print(wrappedInt.value) // Output: 99
print(wrappedString.value) // Output: Swift
Using Generics in Arrays
A Swift generic array allows type safety while storing different objects of the same type.
func printArray<T>(items: [T]) {
for item in items {
print(item)
}
}
printArray(items: [1, 2, 3])
printArray(items: ["Swift", "Generics"])
Best Practices for Using Generics in Swift
- Use generics when writing reusable, type-safe code.
- Avoid overcomplicating code with excessive generic constraints.
- Use
where
clauses for precise type restrictions. - When working with protocols, prefer associated types over generic parameters when flexibility is needed.
- Extend generic types to add additional functionality instead of modifying existing generic structures.
Sign up or download Mimo from the App Store or Google Play to enhance your programming skills and prepare for a career in tech.