Book Image

Swift 4 Programming Cookbook

Book Image

Swift 4 Programming Cookbook

Overview of this book

Swift 4 is an exciting, multi-platform, general-purpose programming language. Being open source, modern and easy to use has made Swift one of the fastest growing programming languages. If you interested in exploring it, then this book is what you need. The book begins with an introduction to the basic building blocks of Swift 4, its syntax and the functionalities of Swift constructs. Then, introduces you to Apple's Xcode 9 IDE and Swift Playgrounds, which provide an ideal platform to write, execute, and debug the codes thus initiating your development process. Next, you'll learn to bundle variables into tuples, set order to your data with an array, store key-value pairs with dictionaries and you'll learn how to use the property observers. Later, explore the decision-making and control structures in Swift and learn how to handle errors in Swift 4. Then you'll, examine the advanced features of Swift, generics and operators, and then explore the functionalities outside of the standard library, provided by frameworks such as Foundation and UIKit. Also, you'll explore advanced features of Swift Playgrounds. At the end of the book, you'll learn server-side programming aspect of Swift 4 and see how to run Swift on Linux and then investigate Vapor, one of the most popular server-side frameworks for Swift.
Table of Contents (9 chapters)

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