Swift Generics
Generics are used to write flexible and reusable code that can work across multiple types. So instead of creating a different function/struct for every type such as Int, String, etc, we can set a Generic type. A generic type is also known as a placeholder type. Typically when we pass typed parameters in a function we do something like
func printInt(aValue: Int) { print(aValue) } printInt(aValue: 1) func printString(aString: String) { print(aString) } printString(aString: "balututorial.com")
Now we can easily use generic parameters instead of creating a different function for each type. For creating a generic function you need to set a placeholder value after the function name in angular brackets as <Element>.
You need to use the same placeholder value as parameters/return types. You can pass more than one placeholder values too. Typically, if the generic parameter placeholder doesn’t represent anything, use T, U, V, etc.
func printAnyThing<T>(a: T) { print(a) } printAnyThing(a: 1) printAnyThing(a: "Balututorial.com")
Another example: The classical way of swapping values:
func swapData<T>(_ a: inout T, _ b: inout T) { let temporaryA = a a = b b = temporaryA } var x = "Balu" var y = "Tutorial" swapData(&x, &y) print("x:\(x) y:\(y)") var valueX = 2 var valueY = 3 swapData(&valueX, &valueY) print(valueX) print(valueY)
func printTandU<T,U>(a:T,b:U) { print("T is \(a) and U is \(b)") } printTandU(a: 1, b: "Swift Generics")

Generic Type Constraints
We can constrain a Generic type to conform to a certain type also.
import Foundation class Person { var name: String? var age: Int init(name: String, age: Int) { self.name = name self.age = age } } func printDetails<T: Person>(person1: T, person2: T) { print("Name: is \(person1.name ?? "nil") and age \(person1.age)") print("Name: is \(person2.name ?? "nil") and age \(person2.age)") } var p1 = Person(name: "Balu Naik",age: 31) var p2 = Person(name: "Balututorial.com",age: 1) printDetails(person1: p1, person2: p2)
T conforms to the type of Person. So you cannot pass any value that isn’t of the type Person in the above code. Doing the following would lead to a crash.
class AClass { } printDetails(person1: AClass(), person2: AClass()) //Error: Class AClass isn't of the type Person.
Subclasses would conform to the generic parameter type. Another example where the generic type must conform to the protocol
func compareAandB<T: Equatable>(a: T, b: T) { print("a == b ? \(a==b)") } compareAandB(a: 2, b: 3) compareAandB(a: "Hai", b: "Hello")
Here the ==
won’t work without the Equatable protocol.
Using Extensions on Generic Types
We can use Extensions on a Generic type in the following way:
import Foundation struct Stack<T> { var items = [T]() mutating func push(_ item: T) { items.append(item) } mutating func pop() -> T { return items.removeLast() } } var stackOfInt = Stack<Int>() stackOfInt.push(2) stackOfInt.push(3) print(stackOfInt) //Stack<Int>(items: [2, 3])
Using the where clause
We can use a where clause for an even stricter Generic type constraint checking. In the where clause we can add additional conditions.
import Foundation protocol Hogwarts {} protocol Muggles {} class Person: Hogwarts { var name: String? var age: Int init(name: String, age: Int) { self.name = name self.age = age } } class MClass: Person, Muggles {} func printDetails<T:Person>(a: T) where T:Muggles { print("a is \(a.name ?? "NA") and age \(a.age)") } var p1 = Person(name: "Balu Naik",age: 31) var p2 = Person(name: "Ron",age: 111) var m2 = MClass(name: "Balututorial.com",age: 1) //printDetails(a: p1) //Error: Argument type 'Person' does not conform to expected type 'Muggles' //printDetails(a: p2) //Error: Argument type 'Person' does not conform to expected type 'Muggles' printDetails(a: m2)
So in the above code, we add a checker wherein the type T must conform to Muggles protocol as well besides conforming to the class Person.
Hence it’ll take only types of class M in the above code. Not of the class Person.