Generics
Generics are one of the most powerful features in Swift, enabling you to write flexible, reusable code. They allow you to write code that can work with any type, subject to requirements that you define. Generics serve to abstract and generalize types, allowing the same code to handle different types in a type-safe manner.
Generic Functions
Generic functions can work with any type. The generic type is specified using angle brackets (<...>) after the function name.
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
var someInt = 3
var anotherInt = 107
swapTwoValues(&someInt, &anotherInt)
Generic Types
You can define custom generic classes, structures, and enumerations.
struct Stack<Element> {
private var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element? {
return items.popLast()
}
}
var stackOfString = Stack<String>()
stackOfString.push("hello")
Type Parameters
In the examples above, T and Element are type parameters. They act as placeholders for the actual type that will be passed when the generic function or type is used.
Extending a Generic Type
You can extend generic types with additional functionalities.
extension Stack {
var topItem: Element? {
return items.last
}
}
Type Constraints
You can define requirements that type parameters must satisfy to be valid replacements for the specified type parameter. These requirements are defined using type constraints.
func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
Here, T: Equatable signifies that the placeholder type T must conform to the Equatable protocol.
Associated Types
Protocols can also be generic using associated types, which serve as placeholders for a type that will be provided later.
protocol Container {
associatedtype ItemType
mutating func append(_ item: ItemType)
var count: Int { get }
subscript(i: Int) -> ItemType { get }
}
Using Type Parameters in Protocols
You can define protocols which are able to work with type parameters.
protocol GenericProtocol {
associatedtype MyType
func performAction(with item: MyType)
}
Generics in Protocols and Protocol Extensions
Protocols can be used in conjunction with generics to create highly flexible APIs. In addition, protocol extensions can provide default implementations, enabling you to build highly reusable and adaptable systems.
extension Container where ItemType: Equatable {
func startsWith(_ item: ItemType) -> Bool {
return count >= 1 && self[0] == item
}
}
In summary, generics make it possible to write flexible and reusable Swift components while also maintaining strong type checking and completion, leading to safer and more efficient code.