How to Use Generics in Swift 3

Written by: Reinder de Vries, March 12 2017, in App Development

Generics Swift 3 iOS

Generics are one of the most powerful features of Swift. In this article you’ll look at how they work, and what you can do with them.

This article is a preview of the new Foundation series here on LearnAppMaking.com. In Foundation you’ll learn programming with Swift 3. It’s a complementary module to the main Zero to App Store course on iOS app development with Swift. Instead of using Swift to build apps, Foundation isolates programming concepts so you can better grasp their syntax and how to use them.

Let’s go!

How to use generics in Swift 3: http://learnappmaking.com/generics-swift-3Click To Tweet

What Are Generics?

Generics let you write clear, flexible and reusable functions. It allows you to avoid duplication of functions, and lets you write your code in a clear and expressive manner.

Swift is a strong-typed language, and that means that when you define a type for a variable – you always must do that – you can’t change it later. Neither can this variable contain different types, because it can only have one type. With generics you can specify a “generic type” – a type that’s loosely defined, and for instance constrained by its relation to other variable types.

Complicated? Read on…

Why Use Generics?

Let’s look at an example to see why you would use generics to solve a programming problem.

This is a function for Mary’s farm:

func marysFarm(goose: Int, pigs: Int)
{
    print("Mary's farm has \(goose) geese and \(pigs) pigs")
}

marysFarm(goose: 5, pigs: 2)
// Outputs: "Mary's farm has 5 geese and 2 pigs"

Straightforward, right?

Now, let’s say Mary wants to also define the amount of geese and pigs she has as decimal-point numbers, or Doubles as we know them.

In order to do that, we need to define a second function with a new name marysFarmAsDoubles and different function parameters!

func marysFarmAsDoubles(goose: Double, pigs: Double)
{
    print("Mary's farm has \(goose) geese and \(pigs) pigs")
}

marysFarmAsDoubles(goose: 3.4, pigs: 9.1)
// Outputs: "Mary's farm has 3.4 geese and 9.1 pigs"

In the example above we define a second function that takes Double parameters, instead of Ints.

Now, Mary also wants to have the option to spell out the numbers as words. Again, we have to define a new third function that takes Strings as function parameters…

func marysFarmAsStrings(goose: String, pigs: String)
{
    print("Mary's farm has \(goose) geese and \(pigs) pigs")
}

marysFarmAsStrings(goose: "three", pigs: "six")
// Outputs: "Mary's farm has three geese and six pigs"

This isn’t ideal. We now have three functions that all have exactly the same functionality, the same code, but their function signatures are all different. Only because Mary wanted to define her farm in Int, Double and String

What if we could code those functions differently? That’s where generics come in.

Generic Functions And Type Parameters

Check out this generic function:

func marysFarm<T>(goose: T, pigs: T)
{
    print("Mary's farm has \(goose) geese and \(pigs) pigs")
}

See how the function definition has changed? It’s got two new characteristics:

  • <T> right after the function name
  • T as parameter types

T is a placeholder type parameter. Instead of saying: the parameter must be of type Int or String, you simply indicate: the type can be anything, as long as both goose and pigs have the same type (they’re both T).

You have to declare a placeholder before you can use it in the parameters of the functions. That’s what happens right after the function name. You put an arbitrary name, like T or P, between angle brackets < >.

Now, let’s check out what happens:

func marysFarm<T>(goose: T, pigs: T)
{
    print("Mary's farm has \(goose) geese and \(pigs) pigs")
}

marysFarm(goose: 5, pigs: 3)
marysFarm(goose: 3.9, pigs: 2.1)
marysFarm(goose: "three", pigs: "seven")

You define the function marysFarm(goose:pigs:) once, and call it three times, each with different parameter types.

Nice! You’ve now saved yourself a whole lot of code, made the code more expressive, and easier to maintain.

Generics Are Everywhere…

Now, the cool thing is: if you’re coding Swift, you’ve been using generics all along without knowing it.

Let’s take the + operator, for instance. To Swift, that operator is just another function. It takes two parameters: the left-hand side of the operator, and the right-hand side.

Like this:

let result = 3 + 5

If that + operator looked a bit different, more like a function, it could just be:

let result = add(3, 5)

Guess what happens if you try to add two Doubles

let result = 2.1 + 3.9

Or even two strings:

let result = "hello" + "world"

The + function is a generic itself, that’s why it can perform addition for all kinds of variable types! As long as the left-hand side and the right-hand side have the same type, the + operator can add them.

OK, here’s another:

let a = [Int]()
let b = [String]()
let c = [Vehicle]()

Arrays are generics too! That’s why you can put virtually any kind of object in an array. The same goes for dictionaries and the - operator, for instance.

Protocols vs. Generics

So why not just use protocols, instead of generics?

Protocols serve a different purpose, although it’s similar. You use protocols to define rules that implementors have to follow. Generics don’t define any rules, they just say: you can put “anything” in this parameter.

Protocols say something about the structure of your code, whereas generics say something about its types, and relations between types.

Protocols do have a function together with generics. Take a look at this function:

func equal<T>(a:T, b: T) -> String
{
    return a == b ? "YES" : "NO"
}

let result = equal(a: 3, b: 3)
print(result)

It’s an alias for the == equality operator, which outputs YES when a is equal to b and NO when they’re not equal, using the ternary conditional operator.

You cannot use generics this way. In order to use the == equality operator, on line 3, a and b must be equatable. Swift needs to be able to find out whether a and b are the same, so whatever T is, it needs to be “equatable.” You could say that anything that’s equatable “has the ability to be equal or inequal.”

Swift has defined a protocol for this characteristic called Equatable. Any class that conforms to Equatable is by definition equatable. Examples are String and Int, of which you can easily figure out if they’re exactly the same. For more complex objects, such as arrays and classes, it’s harder – how do determine if two instances of Car are exactly the same?

When you execute the above code, Swift will tell you:

error: binary operator '==' cannot be applied to two 'T' operands

Below that, in the sandbox, Swift will show a whole list of objects it can equate. Unfortunately, it won’t tell us the protocol T must conform to: Equatable.

Changing the function is easy now, T simply needs to conform to a protocol. You’ve seen that before!

func equal<T: Equatable>(a:T, b: T) -> String

Try it out yourself!

func equal<T: Equatable>(a:T, b: T) -> String
{
    return a == b ? "YES" : "NO"
}

let result = equal(a: "hello", b: "hello")
print(result)

Try out Swift in the IBM Swift Sandbox

Here’s a few examples of other protocols:

  • Comparable, in order to use the <=, > and >= operators
  • Hashable, in order to compare objects, arrays and dictionaries
  • Printable, in order to print the value of an arbitrary variable

Conclusion

When do you use generics as a practical app developer?

Fortunately you seldom define your own generics, or create functions with generics. Unless you’re writing a library or foundational code, you won’t often have to define a function that takes generic input.

You do use generics all the time. As you could see in the examples above, generics are everywhere. It helps to know how generics work!

Read more about generics:

Enjoyed this article? Feel free to share it!

How to use generics in Swift 3: http://learnappmaking.com/generics-swift-3Click To Tweet

Written By: Reinder de Vries

Reinder de Vries is an indie app developer who teaches aspiring app developers and marketers how to build their own apps at LearnAppMaking.com. Since 2009 he has developed over 50 apps for iOS, Android and the web, and his code is used by millions of users all over the globe. When Reinder isn't building apps, he enjoys strong espresso and traveling.

Grab My Free iOS Development Course

Get complementary access to my course, Zero to App Store, and learn how you can build a real-time chat app with Firebase and Swift!

Yes, Send Me The Free Course!

Comments & Thoughts