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.
