Enumerations Revisited - OOP REVISITED - Understanding Swift Programming: Swift 2 (2015)

Understanding Swift Programming: Swift 2 (2015)

PART 4: OOP REVISITED

31. Enumerations Revisited

We saw in an earlier chapter how an enumeration can be created—a new named data type that can contain only one of a set of predefined values that are specified in the definition of that enumeration. Thus, we can create a custom enumeration named DayOfTheWeek and define it as having possible values (known as members) like Monday, Tuesday, Wednesday, etc. that represent all of the days of the week. We can then define a variable as:

var today: DayOfTheWeek = .Friday

We also saw that other values, such as integers, can be attached to each of the enumeration members. Thus the member named Friday might have as an alternative value the integer 5. These are known as raw values.

This is a nice but not very surprising extension of the way that enumerations normally work in C and similar languages.

Clearing Up Some Confusion About Instances, Properties, and Enumerations

We now get to a very different role of an enumeration—its role as a custom type that works much like a class and that has instances.

We can see graphically how enumerations fit in with classes and structures by looking at the table below that shows the different characteristics of classes, structures, and enumerations:

The big things are inheritance (type casting goes along with it), using reference type memory (deinitializer goes along with this), and having stored instance properties.

Structures and enumerations don't have inheritance nor use reference type memory, and are in this way quite different than classes. But a structure is in a sense a simplified class, and uses instances in the same way as classes, which is a major similarity.

Enumerations don't have stored properties, which makes them seemingly very different from structures and classes.

What might be surprising, though, is that enumerations, like the other types, can create instances, even though they do not have stored properties. And although they do not have stored properties, they do have something else that acts very much like a stored property.

A stored property is Swift's version of what in object-oriented programming has traditionally been called an instance variable. An instance variable stores information for a particular instance, and the data in the set of instance variables defined by the class is what makes one instance different from another. So how can enumerations have instances if they have no stored properties?

Or, even if they can create instances, how can these different instances be useful if they don't have values in stored properties to differentiate one from another?

Associated Values are the Instance Variables/Stored Properties of Enumerations

The answer to this is that enumerations have something new, known as an associated value, that plays the role of an instance variable or Swift property for an enumeration.

Associated values work rather like Swift properties but each is defined in a way that makes it useful for specific enumeration members.

Let's take the example that we used before, of an enumeration that represents a particular day of the week.

Now suppose that we want to capture some information, but the information we want to capture depends on the day of the week.

On weekdays we catch a commuter train and we are interested in tracking how accurately the trains arrive at the Embarcadero station in San Francisco (we're concerned about being late for work). So we have an app that, when we tap a button that says we have arrived, captures the time.

However, on Saturdays and Sundays, we walk around the park in our suburban city, where we are fascinated by our suspicion that every time a duck dives to the bottom of Lake Elizabeth, it comes back up after a delay that seems quite constant for each duck, even though different ducks take different amounts of time. (Presumably the ducks are digging for food.)

So on Mondays, for example, we capture a 6-character string that represents the hour, minute, and second of our arrival in San Francisco (e.g., "084501").

On Saturdays and Sundays, however, we capture a different kind of information. For each instance of a duck sighting, we capture the type of duck (a string like "Mallard, male") and then we capture a floating point number which represents the amount of time in seconds that the duck was under water. The ducks always come up within 60 seconds, but we want much more precision than we cared about in the case of the commuter train.

The definition of the enumeration would be:

enum DayOfTheWeek {

case Sunday (String, Double)

case Monday (String)

case Tuesday (String)

case Wednesday (String)

case Thursday (String)

case Friday (String)

case Saturday (String, Double)

}

If the day is Monday, we might create an instance representing our trip into San Francisco with the following:

var today: DayOfTheWeek = .Monday("084501")

This instance captures the value of the enumeration, plus the associated value, that saves information as if it were a single stored property.

If the day is Saturday, we might create an instance representing a single observation of a duck diving in the lake that captures the type of duck and how long the duck was underwater:

var today: DayOfTheWeek = .Saturday("Mallard, Female", 8.4327)

Here we capture two pieces of data, and store them into what are effectively two different stored properties, although we don’t call them that, and they only work with instances set to enumeration values that have the proper associated values.

On weekdays we just create one instance per day. On weekends we create as many instances as we have observations of ducks diving.

If we have an instance, we can get the information from it by using a switch statement:

var today: DayOfTheWeek = .Saturday(("Mallard, Female", 8.4327)

switch today {

case let .Sunday (whichDuck, timeUnderwater):

print("Duck \(whichDuck) took \(timeUnderwater) secs");

case let .Monday (timeArriving):

print("Train arrived Mon at \(timeArriving)")

case let .Tuesday (timeArriving):

print("Train arrived Tues at \(timeArriving)")

case let .Wednesday (timeArriving):

print("Train arrived Wed at \(timeArriving)")

case let .Thursday (timeArriving):

print("Train arrived Thurs at \(timeArriving)")

case let .Friday (timeArriving):

print("Train arrived Fri at \(timeArriving)")

case let .Saturday (whichDuck, timeUnderwater):

print("Duck \(whichDuck) took \(timeUnderwater) secs")

}

Examples of the Use of Associated Values

It may be helpful in visualizing how associated values are used to know about other examples of how they have been used, including those in the real world.

ENUMERATIONS FOR DEFINING OPTIONALS

An enumeration, including an associated value, is used to implement the optional type in Swift. The definition of an optional is as follows:

Enum Optional {

case Some(String)

case None

}

An optional has a value of either .None or .Some. If the value is .None, there is no associated value, and the optional is effectively a nil. If the value is .Some, the wrapped optional value is stored in the associated value for .Some and can be retrieved as part of the unwrapping process.

For example, suppose we create an optional string type and assign a value to it, causing the value to be wrapped:

var message: String? = "Syntax error"

We can later unwrap it with a switch statement:

switch message {

case .Some(let stringValue):

print("The unwrapped value is \(stringValue)")

// Prints “The unwrapped value is Syntax error”

case .None:

print("Variable not unwrapped, it is a nil")

}

ENUMERATIONS FOR NETWORK QR CODES

The Apple documentation on enumerations uses as an example the collection of bar code numbers using two different kinds of bar codes—the older UPC-A format, and the newer two-dimensional QR code style format. The enumeration definition looks like this:

enum Barcode {

case UPCA(Int, Int, Int, Int)

case QRCode(String)

}

If the bar code being read is in the UPCA format, it is captured in four pieces as integers. If the bar code is in the QR Code format, it is captured as a single long string.

This is like having four different integer stored properties associated with the UPCA member, but only a single string stored property associated with the QR Code member.

ENUMERATIONS FOR NETWORK ADDRESSES

One book on Swift (Swift Pocket Reference, by Anthony Gray), describes the use of enumerations to store network addresses. One type of network address, an Ethernet address, is stored in MAC form as a string (it is actually six 2-digit hex values separated by colons), while another type, iPv4, is stored as four 8-bit unsigned values. The enumeration definition looks like this:

enum NetworkAddress {

case MAC(String)

case iPv4(UInt8, UInt8, UInt8, UInt8)

}

An instance of an address in the Ethernet form might be:

var ethernetAddress = NetworkAddress.MAC("00:DD:AA:BE:EA:00")

An instance of an address in the iPv4 form might be:

var ipAddress = NetworkAddress.iPv4(192,168,0,1)

ENUMERATIONS FOR CONNECTION STATUS

Another Swift book (Swift for the Really Impatient, by Matt Henderson and Dave Wood), describes an enumeration to store information related to a wireless device's current connection to a network. The enumeration definition is:

enum NetworkConnection {

case NotConnected

case WiFi(String, Int)

case Cellular(Int)

}

The enumeration members are NotConnected, WiFi, or Cellular. In the latter two cases, the delay in accessing a server via the network is recorded, as an integer, in seconds. In the case of WiFi, the name of the WiFi network is also recorded.

Value Versus Reference Types in Memory

Like structures, but unlike classes, enumerations are value types, meaning that when a reference is made to them a copy is made of the information stored in memory related to the enumeration. Classes, in contrast, are reference types, meaning that they store only a single copy of the data associated with each instance of a class in memory. This goes into the heap memory (See Chapter 19 on "Memory Management" for a discussion of the differences between heap and stack memory.)

Choosing Between an Enumeration, a Structure and a Class

You would only choose to use an enumeration when the data you are dealing with fits naturally into the categories that you will use as members of the enumeration.

The decision about whether to use an enumeration in its role as a type that has instances, with associated values and methods, is a different decision than whether to use an enumeration in its more basic role. It's certainly possible to define an enumeration and then define a class or structure that uses the enumeration's values (perhaps defining a property for the value of the enumeration) and then using properties of a class or structure rather than associated values of an enumeration.

The decision is usually based mostly on the convenience of the data structures, but if one or more of the associated values has a value (e.g., a very large image or a complex type) that is expensive or unwieldy to copy, it may be desirable to use a class even if the data structure is a little more cumbersome.

Using Methods With Enumerations

Instance and also "class" or "type" methods can be used with enumerations. Class/type methods can be used with class/type stored properties as these kinds of properties do exist for enumerations. However, instance methods must work with associated values rather than stored properties (since enumerations don't have stored instance properties).

Thus if we create an instance of one of our DayOfTheWeek enumerations that resulted from a particular day Saturday and an observation of a particular duck:

var particularDay = DayOfTheWeek.Saturday("Mallard", 12.4)

Given an instance of DayOfTheWeek, we would extract the desired information from it using a switch statement:

switch particularDay {

case .Monday(let timeArrived):

print("Time arrived:\(timeArrived)")

case .Saturday(let typeOfDuck, let timeUnderwater):

print("Type of duck:\(typeOfDuck) Time underwater:\(timeUnderwater)")

}

This obviously just covers Monday and Saturday, but you get the idea.

This extracts the values associated with the enumeration that matches and sets them to one or more constants (e.g., typeOfDuck and timeUnderwater in the case of .Saturday.) That constant can then be accessed in a print statement. (This uses a value binding in a switch statement, which is described in Chapter 20, "The Flow of Control Revisited".)

Computed and Lazy Properties and Property Observers With Enumerations

Enumerations can have computed properties, lazy properties, and property observers to the extent that they make sense given their lack of stored properties. These have the same syntax as those described for classes.

Hands-On Exercises

Go to the following web address with a Macintosh or Windows PC to do the Hands-On Exercises.

For Chapter 31 exercises, go to

understandingswiftprogramming.com/31