Over the years, Swift has become a very powerful language with many features, and one of the greatest of Swift's powers is generics.
Generic code is a familiar concept for many developers because it exists in other, older languages like Java, C# and even C++ (where generics are called templates).
Generics solve many problems and enable developers to write code very concisely, resulting in less code duplication and more type-agnostic.
In fact, you’ve been using generics for a long time, even if you didn’t realize it, because Swift’s arrays and dictionaries are both generic collections.
It's true! Let’s take a closer look: Can you create an array that holds Int values? And how about an array that holds String values or UIViewControllers?
See, that's a code structure that generic types allow!
But wait!
That does not mean that Swift has a weak type system! In fact, it’s the opposite. Swift has a strong type system. In other words, once a variable is declared as a type X, you can’t just assign a type Y value to it. Cool?
Let’s create a code sample to illustrate what we are talking about. We're going to create a function that returns the sum of 2 Int parameters:
Nice! But this function only supports the Int number type, and if you send a Double type parameter, you will receive an alert telling you:
cannot convert value of type ‘Double’ to expected argument type ‘Int’.
The first option you have is to create a new function with Double type parameters. But this is code duplication and that is not good (see the DRY principle).
So let’s turn our previous function into a generic function. Instead of setting the Int type, we will set as a placeholder type T and inform the compiler that the function is generic, which means defining it with <T>.
But Swift doesn’t know that the generic type T has a ‘+’ binary operator.
Can you imagine sending two random type parameters like UIViewController and summing them? That does not make too much sense, right?
So for this particular kind of generic function, you will need to set a protocol conformance or a type constraint to limit our function usage to only work with Numeric types. Numeric is a built-in protocol (Swift4+) for any numeric values.
Now, our function works like a charm:
Wow! This is not just powerful. It is really beautiful. Don’t you think? Even if you don't agree now, you will in a minute.
Now, let’s add more complexity here: this generic function works with all types of numbers because of the conformance with Numeric Protocol, which is natively provided by Swift.
But what if you don’t have a free protocol to use as a Type Constraint?
Well, you will need to create this protocol on your own. Yes, this requires more effort, but it isn't too difficult:
You'll need to do this when you do not natively have a protocol, but for most cases, Swift already provides a collection of basic protocols as constraints, like Equatable, Comparable, Hashable, Numeric, and many others.
Now, let’s try to think of a new scenario where we want to simulate how a restaurant works. Since we just started our amazing restaurant, we only have one person to cook, so what would be the best basic data structure for preparing orders?
The best structure is one where the first customer receives their order first, right? First-In-First-Out. That’s the definition of a queue. Cool!
So we will need a protocol to enqueue, dequeue, and show the current orders we have.
In the future, we're hoping to add different menus, but for now, we just have one simple menu. Because of that, our protocol needs a placeholder type to work with any present and future menu items. This generic placeholder type is called Associated Type.
The basic format of the Orders protocol and the implementation in our RestaurantOrders class will look like this:
But we only prepare food from our SimpleMenu. It does not make sense for the customer to come to our restaurant and order food that we can't prepare, right?
So we will determine the type of items we accept in our RestaurantOrders instance, which should look like this:
Now we have our restaurant running full steam, and when we launch the new ItalianMenu, everything will be ready. Check this out:
Amazing! We've made our restaurant fully functional!
This is the power of generics that I alluded to at the beginning of this post. Generics can turn your code into much cleaner code. Generics are also important for the construction of useful libraries because they allow the library to adapt to application-specific data types without losing type safety.
To understand how important this concept is in Swift, you just need to look at the Swift standard library. Also, check out the Swift Language Documentation to better understand this topic.
P.S. You can download a playground file with the above code here.