Intro to Generics
Introduction
Welcome to Lesson 6 of The Swift Fundamentals with Bob. When you think of the word, “generic”, what makes you think? Well, I’m sure you’ve thought of versatile code. What? Don’t you dare worry. I will walk you through. Remember, the number one goal is to write as little as possible, but produce as much.
Problem
I smell something.
For the reference, read more about code smell
Access Elements
Let us reflect on how we rolled in the past.
let highSchoolGPA = [2.8, 3.2, 3.5, 3.8, 3.5]
let favoritePeople = ["Elon Musk", "Steve Jobs", "Kevin O'leary"]
let favoriteNumbers = [3, 20]
The Worst way to access elements
highSchoolGPA[0]
highSchoolGPA[1]
highSchoolGPA[2]
Loop and Print
This is slightly better.
func printDoubleElement(array: [Double]) {
for GPA in array {
print(GPA)
}
}
func printStringElement(array: [String]) {
for person in array {
print("I love \(person)")
}
}
func printNumberElement(array: [Int]) {
for number in array {
print("I like \(number)")
}
}
Needlessly many functions. It goes against the DRY principle.
Introducing Generics
A generic function allows you to pass any value regardless of types.
func genericFunction<anything>(value: anything) {
print(value)
}
func genericFunctions<WHATEVER>(value: WHATEVER) {
print(value)
}
Swift infers the type of anything
or WHATEVER
based on the input.
genericFunction(value: "Bob") // anything is String
genericFunctions(value: true) // WHATVER is Bool
Generic Loop
Let us create a generic function.
func printElement<T>(array: [T]) {
for element in array {
print(element)
}
}
Call the function
printElement(array: highSchoolGPA) // 2.8, 3.2, 3.5, ...
printElement(array: favoritePeople) // 'Elon Musk", "Steve Jobs", ...
Generic code enables you to write flexible, reusable functions, classes, enums, protocols, and structs subject to requirements that you define.
Now only you can create generic functions, but also generic classes and structs. Let us begin with a non-generic struct.
Non-Generic Struct
Create a Family
struct that contains members
whose type is [String]
. Add a method that appends to the array.
struct Family {
var members = [String]()
mutating func push(member: String) {
members.append(member)
}
}
Important: The
mutating
keyword is added to function ofenum
andstruct
that mutates its own property/properties.
Non-Generic Instance
var myFam = Family()
myFam.push(member: "Bob")
myFam.members // ["Bob"]
The struct above only works with String
. Some families may have names in Int
. Then, you must create a new struct. It goes against the DRY principle.
Generic Struct
struct genericFam<T> {
var members = [T]()
mutating func push(member: T) {
members.append(member)
}
}
Instead of using WHATEVER
or anything
, T
is a standard among iOS developers.
Define Type Explicitly
Now, let us create an object from the generic struct. You have to specify the type based on the rule in Lesson 1.
var myGenericFamily = genericFam<String>()
myGenericFamily.push(member: "Bobby")
var genericFamily = genericFam<Int>()
genericFamily.push(member: 123)
Define Type Implicitly
However, the type can be inferred based on the input.
let myHappyFamily = genericFam(members: [1, 2, 3, 4, 5]) // T becomes Int
Generic Extension
You may add an extension to generics for more features. Let us grab the first element in the members
property.
extension genericFam {
var firstElement: T? {
if members.isEmpty {
return nil
} else {
return members[0]
}
}
}
let geekFamilyMember = genericFam(members: ["Bob", "Bobby"])
let firstElement = geekFamilyMember.firstElement // "Bob"
Type Constraints {#type-constraints}
So far, you could enter any value to define T
. But, you may restricts the type you only want to interact with.
First, design two classes.
class LOL {}
class BabyLol: LOL {}
Now, let’s create a function that only allows you to enter LOL
.
func addLOLClassOnly<T: LOL>(array: [T]) { }
When you run the addLOLClassOnly
function, you can’t add anything besides whose type is LOL
.
addLOLClassOnly(array: [LOL(), LOL(), LOL(), BabyLol()])
addLOLClassOnly(array: [1, 2, 3, 4, 5]) // Error
Important:
BabyLol()
has been automatically upcasted toLOL
.
Resources
Intro to Generics in Swift with Bob
Source Code
Conclusion
You now understand how to maintain yourself dry through generic functions and structs. In later chapters, you will more about advanced generics along with enums
and protocols
. If you wish to review generic the syntax, I recommend you to watch the lecture again or refer to the article I’ve attached.
In the next lesson, you will learn how to provide shortcuts within classes and structs instead of calling methods and properties.
Note: Learn Swift with Bob is available on Udemy. If you wish to receive a discount link, you may sign up here.