Tutorial

Mastering Swift: Enumerations, Closures, Generics, Protocols and High Order Functions


Welcome to the “Mastering Swift” tutorial series! This tutorial is a bit different from the ones usually featured on AppCoda, because instead of teaching you about the iOS APIs or a specific iOS topic, this tutorial will teach you about Swift, Apple’s new programming language for developing apps. We will be exploring some tips, tricks, and techniques that you can follow to make your Swift code even Swiftier. Swift was designed with safety, clarity, and stability in mind, and we will use several of Swift’s key features to achieve these goals.

To get started, fire up a new Xcode and create a Playground file. You won’t need a starter project to follow this tutorial. I will just walk you through the code and test it using Playgrounds.

Enumerations

If you don’t know about them already, enumerations, or enums, are a special type of value in Swift that allow you to represent multiple cases, or possibilities. Enums are similar to Bool in that enum values have to be one of multiple cases. Bool can only be true or false, but enums can be any of the cases you define. Let’s take a look.

Assuming you have open Xcode’s Playgrounds, we’ll start by declaring an enum:

As you can see, declaring an enum is easy. In our example above, we have declared a new enum called DownloadStatus with 4 cases, downloading, finished, failed, and cancelled. We can use our enum just like we use any other type such as String or Int:

Editor’s note

At this point, you may wonder why you need to use enum to define the direction. Probably you’re thinking why you can’t declare direction as an array with four items like this:

let downloadStatus = [“downloading”, “finished”, “failed”, “cancelled”]
let currentStatus = downloadStatus[0]

Yes, you can do that. But there are two disadvantages here. First, you have no idea about what downloadStatus[0] is, unless you refer to the downloadStatus array. If you compare downloadStatus[0] to DownloadStatus.downloading, it is very obvious that the latter is more readable.

Secondly, as currentStatus is of type String, you can actually assign whatever string value to the variable. You can’t limit the value to “downloading”, “finished”, “failed” and “cancelled”, unless you perform some additional validations. On the other hand, if enum is used, we can only set myDirection to .downloading, .finished, .failed or .cancelled, but nothing else.

When declaring enums, the actual name of the enum (DownloadStatus in this case) should begin with a capital letter. Additionally, it should be singular. This means that naming our enum downloadStatus or DownloadStatuses would be grammatically incorrect. It’s important to follow consistencies and conventions when programming, and this is one that should be universally followed.

Additionally, our enum cases should begin with lowercase letters, as of Swift 3. Prior to Swift 3, the convention was to capitalize enum cases, but that has changed. If you’re developing with Swift 3 or higher, be sure that your enum cases begin with lowercase letters.

Now that you know what enums are, let’s explore how to use them. Swift allows us to switch over enums. Take a look:

This allows us to write a conditional statement that’s much more powerful than a simple if statement. The compiler will enforce that we handle each of the cases in our enum in our switch statement, making sure that we don’t leave out any possible cases. This contributes to the overall safety of our code, making sure we don’t miss anything.

Up until this point, you may be thinking that enums don’t really add new functionality to Swift. Sure, they can make our code safer, but you could always use a String or a few Bools to represent data stored by an enum. Now, let’s take a look at one of the most powerful features of Swift enums: associated values. Associated values allow us to store additional data within an enum. Let’s declare a new enum, called WeatherCondition, that allows us to specify a weather condition, along with some extra information:

In this example, we actually declared 2 enums: Cloud and WeatherCondition. Don’t pay much attention to the Cloud enum. Instead, look at the WeatherCondition declaration. We declared 3 cases, and each of them stores some kind of additional information aside from the case of the enum itself. In our sunny case, we store an Int, called temperature. In our rainy case, we store a Float, called inchesPerHour, in our cloudy case, we store 2 values: cloudType, a Cloud, and windSpeed, a Float.

As you may have observed, associated values let us store additional information in our enums very easily. Let’s see how to use them:

We can switch on associated value enum cases, just like we can on normal ones:

Hopefully, you can see the value that enums add to your app. They can make your code safer, clearer, and more concise. Maybe you already knew about enums, but were unaware of the assocated values they offer, or how to switch over them. Regardless, try to incorporate them into your apps, as they’re incredibly useful.

Closures and Higher Order Functions

One of Swift’s key features is the closure. In Objective C, closures are known as blocks. The concept is similar, but they’re much more powerful in Swift. Simply put, a closure is a function without a name. Swift closures are first class types, meaning that they can be assigned to variables just like any other type. Closures can also be passed into functions.

The type of a closure is expressed as (parameters) -> returnType. For example, a closure that takes a String and a Float as parameters and returns Void has the following type: (String, Float) -> Void. You can write functions that accept closures as parameters:

The function above takes a string and a closure, and then it calls the closure, providing the string parameter to the closure. Here’s an example of using it:

In this case, we provide the closure to the function just like we provide a normal parameter. Then, within the function, we call the closure, just like we would call any other function declared with func. This example illustrates that closures really are just unnamed functions. As a matter of fact, closures in other languages are sometimes referred to as anonymous functions, because that’s really what they are.

Swift also has something called trailing closure syntax, which is syntax sugar for closures. This allows for cleaner and more elegant closure expressions when a closure is the last parameter of a function. Here’s an example, using the same function:

Trailing closure syntax lets us eliminate the parentheses around the closure parameter in a function, but only if it’s the last parameter. When writing functions, make sure you put the closure parameter last (if you have one) so that people who call your code can do so in a clean and concise manner.

Because closures in Swift are so powerful, the Standard Library provides higher order functions. Put simply, a higher order function is a function that takes another function as a parameter. HOFs are present on collection types in Swift, offering functions such as map, filter, forEach, reduce, and flatMap. We will be walking through each one now.

Map

Let’s start with map. Type the following code in Playgrounds and see what you get.

In the code above, it is an example of the map function. As you can see, it is so simple to multiple each of the item in the mapNumbers array by 2 via the map function. You can do that using for loop. The map function simply saves you a lot of code.

The map function takes an input closure, which has one parameter of the same type as the Element of the collection being mapped. This closure should also return a value of the same type. In our case, the parameter is unnamed, meaning we do not explicitly provide a name for it. For this reason, we can refer to it as $0. Subsequent unnamed parameters can be referred to as $1, $2, $3, and so on.

On top of that, you may notice that there is no return value of the closure. For one line closures, we don’t need to use the return keyword, as it’s implied. Finally, we use trailing closure syntax, which allows us to decrease the length of our code further. All of these syntactical shortcuts allow us to write really short, concise closure expressions. Here’s what the same expression let doubledMapNumbers = mapNumbers.map { $0 * 2 } would look like without these syntax improvements:

As you can see, Swift provides us with a lot of ways to shorten our closure expressions, allowing for clean code that’s easy to read.

Filter

Let’s explore more higher order functions. Declare another array, filterNumbers.

In this example, we filter filteredNumbers, keeping only the items that are greater than 3. This example also makes use of Swift’s various syntax improvements to make sure that our code is concise.

forEach

Let’s try forEach, with an array called forEachNumbers:

Using forEach is very similar to using a for loop. Again, we used many syntax improvements to make sure we have clean code.

Reduce

Now, let’s give reduce a try, with reduceNumbers:

reduce is used to reduce a collection down into a single value. In our case, we add all of the numbers together in reduceNumbers into one value, and store it in reducedNumber.

The parameter (i.e. 0) we provide for the reduce function is an initial value. reduce starts with our initial value and performs the specified operation for each item in the collection.

flatMap

One more to go! Let’s use flatMap now. Declare an array called flatMapNumbers. This time, our starting array is a little different than the earlier example that it contains nil values:

flatMap traverses through a collection, using the input closure to manipulate values. The resulting collection only contains non-nil values. You’ll notice that that the resulting array contains no nil elements. flatMap is particularly useful for removing optional values from a collection.

Chaining Them Together

Okay, I promised that flatMap would be the last example, but I have one more to show you, and it’s actually a combination of several transforms. You can chain higher order functions to create powerful transformations that aren’t possible with one transform. Here’s an example:

Hopefully, these examples have illustrated the power of closures and higher order functions in Swift. You can use these techniques in your apps to clarify your code and make it easier to maintain. Good luck!

Generics

Generics are an interesting concept, and they allow you to reuse your code for multiple types. Let’s look at an example:

Our swapInts function takes 2 Ints and swaps them. Here is a sample usage:

But what if we want to swap strings? Probably you will write another function like this:

As you can see, both functions are identical, except for the fact that they have different parameter types. Can you write a swap function that supports multiple types of parameters? This is where we can apply Generics to write more flexible code. We can turn the swap function into a more generic function like this:

Let me explain what we just wrote. We started by declaring what looks just like a normal Swift function, but, as you can see, we have the letter T in between angle brackets. What does this mean? Angle bracket’s are Swift’s syntax for generics. By putting the letter T in between angle brackets, we’re telling Swift that we are forming a generic type, called T. We can now reference this type T in our function. As you can see, we use it to denote the types of our parameters. The rest of the function is just basic declarations. We can now use our swapAnything function to swap anything we want.

Let’s look at a more sophisticated example of using generics. But first, let’s look back. For those of you who are familiar with Objective C, remember how items accessed from an NSArray would be returned as id? If we write this:

We would always have to cast elements from the array into their actual types manually. This is unsafe. What if you don’t know what types of items are in the array? What type would you cast it to? What if you forget to cast it and you attempt to use the object anyway? Your app might crash because of an undefined selector or similar issue.

This is where Swift’s Generics come to rescue. Let’s take a look at the following example:

Here, we declare a stack, that allows you to push any items to it, and pop an item off.

Note: You should read about stacks on Wikipedia if you’re unfamiliar with how they work.

There’s one issue here. The item stored in the stack, and the element we pop, is of type Any. How do we know what type the element is? The right answer is…we don’t know. It could be a String, an Int, or a Float.

Editor’s note: Swift provides a type called Any for working with nonspecific types.

What if we have no idea about the items in the stack? What can we do? Every time when you need to use element, you will probably need to discover its type like this:

It doesn’t seem like a good solution, and it is not a good practice to store items in an array of Any type in Swift. A better way to do it is to limit the stack to a single type. In this case, we will have to create a stack for String, another for Int, another for String. And here, generics in Swift will allow you to create a generic stack:

It isn’t much more complex than our original stack, but the stack is now type safe. Let’s see it in action:

Now that we use generics, our pop function returns a String instead of Any. This adds a lot of type safety to our code and it eliminates unnecessary casting.

Hopefully, you’ll find a way to use generics in our own apps. They’re extremely powerful and they can be used to solve a lot of problems elegantly. Give them a try!

Value vs Reference Types

If you’ve been working with Swift since the early days, you may have heard about the Standard Library’s extensive use of value types. If you’re wondering what they are, now is a good time to find out, as the language is maturing and it doesn’t look like value types are going away anytime soon. To understand value types, you need to understand reference types. Let’s take a look at an example for Objective C:

Uh oh! What happened? This is a prime example of the danger of reference types. You see, when we set myOtherString to myString, the compiler didn’t copy the value of myString and provide one copy to myOtherString. Instead, the compiler told myOtherString to reference myString. When myString changed, so did myOtherString. This type of referencing can cause a lot of bugs if developers do not use it carefully, so the designers of the Swift language chose to use value types instead. Let’s look at the same example in Swift:

Great! No more bugs about weird reference semantics. This makes most code easier to write and maintain for developers. In Swift, structs are value types, and classes are reference types. This knowledge isn’t immediately useful in most day to day development work, but it’s helpful to understand. It can also help you make the choice when deciding between a class and a struct.

Protocols

Swift has another interesting feature known as protocol. Protocols are a new way of viewing class hierarchy and inheritance. In a conventional object oriented programming environment, you define classes, which describe objects and the functionality they provide, along with the properties they have. Then, you subclass your classes, inheriting all of their functionality and properties. Your subclasses can then provide additional functionality and properties, or override parts of their superclasses. These subclasses can then be subclassed further. Let’s look at an example of a typical class hierarchy:

This example illustrates a hierarchy in which we define a base class, Animal, and then subclass it. The Dog subclass overrides the default functionality of the Animal class, and also adds a bite() method, which allows dogs to bite things. The Cat subclass also overrides the base class, but it adds a scratch() method, allowing cats to scratch people.

Do you see any issues with this example? It may not seem like there is anything immediately wrong with this structure, but in reality, there are a lot of things that could be improved. What if someone else subclasses Animal but forgets to override makeSound() and / or move()? In this case, it will just print “Implement me!”

This is a simple example, but imagine what could happen if Animal had dozens of functions and properties that needed to be overriden. What would happen?

In Objective C, these situations presented us with things called abstract base classes. Abstract base classes are like Animal in the fact that they do not provide much functionality on their own. They provide a list of things that need to be overriden, without actually providing functionality on their own. A typical Objective C base class may throw an assertion or otherwise crash the program it’s in if it’s used directly. For this reason, abstract base classes are documented as abstract, and developers who use them are told to never interact with them directly, and instead interact with one of their subclasses.

In Swift, there is no abstract class. However, Swift (and Objective C) both provide a better way to approach situations such as this one: protocols. While protocols are present in both Swift and Objective C, Swift protocols are much more powerful. For the same problem, we can implement Animal as a protocol like this:

Now, what’s different? We still have a dog and cat which both provide the same functionality that their subclassed counterparts did. But, this time, these animals both adopt a common protocol. You see, a class defines what an object is, but a protocol defines what an object does. You can still use the protocol similar to what we did before:

Both Cat and Dog conform to the Animal protocol. And, it is mandatory for them to implement both makeSound and move methods. When adopting a protocol, the compiler enforces that any class or struct should provide the implementation of the required method as specified in the protocol declaration. Therefore, it is impossible to have a struct or class like this:

swift-protocol-error

The compiler will give you an error to make sure you provide the required implementation of the protocol.

We’ll discuss protocols further in an extension of this tutorial, showcasing more situations where protocols can be helpful. Just remember that neither subclassing nor protocols can solve every single programming problem. Sometimes, you need both. Don’t get too hung up on one or the other, as both are incredibly useful and powerful.

Wrapping up

I hope you learned a lot in this tutorial. We talked about a lot of important Swift topics that can enable us to do some really powerful things in our apps. Whether you learned about enums, closures, generics, reference / value types, protocols, or some combination of these items, I hope this tutorial taught you something that you can use every day. Feel free to comment below with any questions.

Editor’s note: Love this tutorial? You will probably like our Swift book.
Tutorial
SwiftUI First Look: Building a Simple Table View App
Tutorial
Using Sleep Analysis in HealthKit with Swift
iOS
Understanding Self Sizing Cells and Dynamic Type in iOS 8
  • Attila Marosi

    Hello Pranjal,
    I found a typo at the “Map” section (missing parenthesis);
    let doubledMapNumbers = mapNumbers.map({ $0 * 2 })


    • Pranjal Satija

      Attila, the parentheses are not necessary in the example you quoted. Swift has *trailing closure syntax*, which is explained in the article. It has a special syntax feature that allows developers to omit parentheses at the call site of a function if its last argument is a closure.


  • Richard Bukovansky

    Hello,
    there is typo in that Weather enum or in explanation of it:

    case sunny(temperature: Float) <– Float
    In our sunny case, we store an Int, called temperature. <– Int


  • Michael Lev

    Example in “Closures and Higher Order Functions” does not work…
    func myFunction(_ stringParameter: String, closureParameter: (String)->Void) {
    closureParameter(stringParameter)
    }
    myFunction(“Hello, world!”, closureParameter: {(string) in
    print(string) //why it prints “0” instead of “Hello, world!” ??
    })


  • Virender Dall

    i read this & i have two doubts, can you please help me to clarify these

    1. How can it print [8, 10]?

    let chainNumbers = [1, nil, 2, nil, 3, nil, 4, nil, 5]
    let doubledNumbersOver8 = chainNumbers.flatMap { $0 }.filter { $0 > 3 }.map { $0 * 2 }
    print(doubledNumbersOver8) //prints [8, 10]

    2. For me this is printing “Hello, world!”

    NSString *myString = @”Hello, world!”;
    NSString *myOtherString = myString;
    myString = @”Guess what? We have a problem.”;
    NSLog(myOtherString); //prints “Guess what? We have a problem.”


  • Vladimir

    VladimirVladimir

    Author Reply

    NSArray cannot store primitive types


  • ensorcell

    ensorcellensorcell

    Author Reply

    I can’t get forEach to work like it does in the example:

    let forEachNumbers = [1, 2, 3, 4, 5]
    forEachNumbers.forEach { print($0) } // the result only states “(6 times)”


    • Gecko Mancer

      ensorcell, the right side of the screen is a “verbose” description of what your code is doing; it is not running the code. To see the Run output, look in the Debug Area (bottom of screen).

      If you don’t see it, show it (⇧⌘Y, or View -> Debug Area -> Show Debug Area).

      Be sure to press play, or set it to run automatically!


Shares