Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Swift 4 Programming Cookbook

You're reading from   Swift 4 Programming Cookbook 50 task-oriented recipes to maximise Swift 4 productivity

Arrow left icon
Product type Paperback
Published in Sep 2017
Publisher Packt
ISBN-13 9781786460899
Length 384 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Keith Moon Keith Moon
Author Profile Icon Keith Moon
Keith Moon
Arrow right icon
View More author details
Toc

Table of Contents (9) Chapters Close

Preface 1. Swift Building Blocks FREE CHAPTER 2. Building on the Building Blocks 3. Data Wrangling with Swift Control Flow 4. Generics, Operators, and Nested Types 5. Beyond the Standard Library 6. Swift Playgrounds 7. Server-Side Swift 8. Performance and Responsiveness in Swift

Enumerations

Enumerations are a programming construct that let you define a value type with a finite set of options. Most languages have enumerations (usually abbreviated to enums), including C and, by extension, Objective-C.

An example of an enum from the iOS/macOS SDK is NSComparisonResult, which you would use when sorting items. When comparing for the purposes of sorting, there are only three possible results from a comparison:

  • ascending : The items are ordered in ascending order
  • descending : The items are ordered in descending order
  • same : The items are the same

There are a finite number of possible options for a comparison result; therefore, it's a perfect candidate for being represented by an enum:

enum ComparisonResult : Int { 
case orderedAscending
case orderedSame
case orderedDescending
}

Swift takes the enum concept and elevates it to a first class type. As we will see, this makes enums a very powerful tool for modeling your information.

This recipe will examine how and when to use enums in Swift.

Getting ready

This recipe will build on top of the earlier recipes, so open the playground you have used for the previous recipes. Don't worry if you haven't tried out the previous recipes; this one will contain all the code you need.

How to do it...

Earlier, we created a Person object to represent people in our model and a PersonName struct to hold information about a person's name. Now, let's turn our attention to a person's title, for example, Mr., Mrs., and so on, which precede someone's full name. There are a small and finite number of common titles that a person may have; therefore, an enum is a great way to model this information.

Enter the following into the playground:

enum Title { 
case mr
case mrs
case mister
case miss
case dr
case prof
case other
}

We define our enumeration with the enum keyword and provide a name for the enum. As with classes and structs, the convention is that this starts with a capital letter, and the implementation is defined within curly brackets. We define each enum option with the case keyword, and the convention since Swift 3 is that these start with a lowercase character. Now that we have defined our enum, let's see how to assign it:

let title1 = Title.mr 

Enums can be assigned by specifying the enum type, then a dot, and then the case. However, if the compiler can infer the enum type, we can omit the type and just provide the case, preceded by a dot:

let title2: Title 
title2 = .mr

How it works...

When used in C and Objective-C, enums are defined as a type definition on top of an integer, with each case being given a defined integer value. In Swift, enums do not need to represent integers under the hood; in fact, they do not need to be backed by any type, and can exist as their own abstract concepts. Consider the following example:

enum CompassPoint { 
case North, South, East, West
}
Note that we can define multiple cases on the same line by separating them with commas.

For Title, an integer-based enum doesn't seem appropriate; however, a string-based one may be. So, let's declare our enum to be string based:

enum Title: String { 
case mr = "Mr"
case mrs = "Mrs"
case mister = "Master"
case miss = "Miss"
case dr = "Dr"
case prof = "Prof"
case other // Inferred as "other"
}

The enum's raw type is declared after its name and a : separator. The raw types that can be used to back the enum are limited to types that can be represented as a literal; this includes Swift base types--String, Int, Float, and Bool.

These types can be used to back an enum because they conform to a protocol, called RawRepresentable. We will cover protocols later in the chapter.

Cases can be assigned a value of the raw type; however, certain types can be inferred, and so do not need to be explicitly declared. For Int-backed enums, the inferred values are sequentially assigned starting at 0:

enum Rating: Int { 
case worst // Infered as 0
case bad // Infered as 1
case average // Infered as 2
case good // Infered as 3
case best // Infered as 4
}

For string-based enums, the inferred value is the name of the case, so the other case in our Title enum is inferred to be other.

We can get the underlying value of the enum in its raw type by accessing its rawValue property:

let title1 = Title.mr 
print(title1.rawValue) // "Mr"

There's more...

As I mentioned in the introduction to this recipe, Swift treats enums as a first-class type, and therefore they can have functionalities that are not available to enums in most programming languages. These functionalities include having computed variables and methods.

Methods and computed variables

Say that it is important for us to know whether a person's title relates to a professional qualification that the person holds. Let's add a method to our enum to provide that information:

enum Title: String { 
case mr = "Mr"
case mrs = "Mrs"
case mister = "Master"
case miss = "Miss"
case dr = "Dr"
case prof = "Prof"
case other // Inferred as "other"
func isProfessional() -> Bool {
return self == Title.dr || self == Title.prof
}
}

For the list of titles that we have defined, Dr and Prof relate to professional qualifications, so we have our method return true if self (the instance of the enum type this method is called on) is equal to the dr case, or equal to the prof case.

This functionality feels more appropriate as a computed property since whether it isProfessional or not is intrinsic to the enum itself, and we don't need to do much work to determine the answer. So, let's change this into a property:

enum Title: String { 
case mr = "Mr"
case mrs = "Mrs"
case mister = "Master"
case miss = "Miss"
case dr = "Dr"
case prof = "Prof"
case other // Inferred as "other"

var isProfessional: Bool {
return self == Title.dr || self == Title.prof
}
}

Now, we can determine whether a title is a professional title by accessing the computed property on it:

let loganTitle = Title.mr
let xavierTitle = Title.prof
print(loganTitle.isProfessional) // false
print(xavierTitle.isProfessional) // true

We can't store new information on an enum, but being able to define methods and computed properties that provide extra information about the enum is really powerful.

Associated values

Our string-based enum seems perfect for our title information, except that we have a case called other. If the person has a title that we hadn't considered when defining the enum, we can select other, but that doesn't capture what the other title is. We will need to define a whole other property to hold the value of the other title, but there would be nothing to force it to be used when the other case is selected, and nothing to stop it from being used when a different Title case is selected.

Swift enums have a solution for this situation--associated values; we can choose to associate a value with each enum case, allowing us to bind a non-optional string to our other case.

Let's rewrite our Title enum to use an associated value:

enum Title { 
case mr
case mrs
case mister
case miss
case dr
case prof
case other(String)
}

We have defined the other case to have an associated value by putting the value's type in brackets after the case declaration. We do not need to add associated values wholesale to every case; other case declarations can have associated values of a different type, or none at all.

Enums containing associated values cannot have a raw type as they are now too complex to be represented by one of these base types, so our Title enum is no longer string-based.

Let's look at how we assign an enum case with an associated type:

let mister: Title = .mr
let dame: Title = .other("Dame")

The associated value is declared in brackets after the case, and the compiler enforces that the type matches the type declared in our enum definition. As we declared the other case to have a non-optional string, we are ensuring that a title of other cannot be selected without providing details of what the other title is, and we don't have an unneeded property hanging around when anything else is selected.

See also

You have been reading a chapter from
Swift 4 Programming Cookbook
Published in: Sep 2017
Publisher: Packt
ISBN-13: 9781786460899
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime