CODEX
Swift: Generics Made Simple
Avoid code duplication by writing functions and types that work with all types using generics.
Generics might sound like a difficult and advanced topic in Swift. In reality, unbeknownst to you, you have most likely been using generics countless times before.
Today I will show you how simple the idea really is and how you can start using generics too.
Generics in Swift
What Are Generics in Swift
According to Swift’s official docs:
Generic code enables you to write flexible, reusable functions and types that can work with any type, subject to requirements that you define. You can write code that avoids duplication and expresses its intent in a clear, abstracted manner.
Let’s see what this means in action.
What Problem Do Generics Solve
Say you want to implement functionality for printing info about an argument passed to a function. The argument type can be any type, e.g. a String
or Int
.
In order to make it work you need a separate printer function for each possible type there is:
func infoString(_ a: String){
print("Argument type is String with a value of \(a)")
}func infoInt(_ a: Int){
print("Argument type is Int with a value of \(a)")
}func infoDouble(_ a: Double){
print("Argument type is Double with a value of \(a)")
}// ... and so on
But this looks bad. There is a lot of repetition, and furthermore, there are so many types that there’s no way to write a dedicated function for each type.
This is where generics come in handy. Instead of writing a number of functions to perform the same thing for different types, you can create a single generic function that accepts a parameter of any type as its argument(s).
Let’s create a generic info
function that accepts an argument of any type:
func info<T>(_ a: T){
print("Argument type is \(type(of: a)) with a value of \(a)")
}
Now can use this generic info
function to print info about different types of arguments:
info(3) // Argument type is Int with a value of 3
info(3.2) // Argument type is Double with a value of 3.2
info("Yeah") // Argument type is String with a value of Yeah
Let’s briefly examine the generic info
function’s implementation:
- Instead of specifying the type of the argument
a
, a generic placeholder typeT
is used to highlight that the type can be any type. - The placeholder type
T
also needs to be present in the function name. This is done by adding<T>
after the name of the function.
It is so simple! Now you understand the basic idea of generics and how to use them.
Another Example of Generics
Let’s say you have arrays of items that contain strings or ints and you want to create a function for printing the contents of each of the arrays.
let numbers = [1, 2, 3, 4, 5]
let names = ["Nicolas", "Anne", "Robert", "Sofie"]
As the arrays contain different types of elements, you once again need to create separate functions for each type of array:
func printInts(_ arr: [Int]){
arr.forEach { print($0) }
}func printStrings(_ arr: [String]){
arr.forEach { print($0) }
}printInts(numbers) // prints all the numbers
printStrings(names) // prints all the names
But luckily you just learned about generics in Swift. Instead of duplicating the functions for each array type, you can write a generic printing function:
func arrPrint<T>(_ arr: [T]) {
arr.forEach { print($0) }
}
Isn’t it handy? Now you can print the contents of each of the array with this generic function:
// A test that it really works:arrPrint(numbers) // prints all the numbers
arrPrint(names) // prints all the names
Conclusion
Using generics it is possible to avoid unnecessary repetition by writing a generic function or a type that works with all types.
As a matter of fact, you’ve most likely used generics already without noticing. For example, an Array is a generic collection type: You can create an array that contains different types of elements, e.g. Doubles or Integers.