• 20 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

Ce cours est en vidéo.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

J'ai tout compris !

Mis à jour le 04/02/2019

Represent cases

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

We know how to take advantage of inheritance and utilize the components of a parent class while adding new unique capabilities to subclasses.

But what about the situations when subclasses require the same activity but executed differently?

In our puppies and kittens examples, one can bark, the other can meow! Both are a form of communication and we can assume that all animals can communicate one way or another.

In this case, instead of implementing methods for barking and meowing separately in subclasses, we can move it to the parent class (animal) and name it communicate instead:

class Animal {
    // properties
    var color = "brown"
    var weight = 20
    
    // methods
    /* common methods */
    
    func communicate() {
        
    }
}

class Dog: Animal {
}

class Cat: Animal {
}

But how do we know whether we need to bark or meow when that method is called?

We can pass that data as a parameter to the communicate function. We could use strings as the data type to pass the values: "bark", "meow", which as we know, are easy to mistype.

However, there’s a better way: defining a special custom type.

So far we know only one custom type (or a named type) we can define: classes.

What other custom types are out there?

In Swift there are 3 models that allow for defining named types:

  • classes

  • structures

  • enumerations

You are already knowledgeable about classes.

Structures are very similar to classes and are very useful in Swift, however, we won’t use them in this course.

 So, this leaves us with enumerations for this chapter!

What are enumerations?

Enumerations (or, enums for short) are a data model that allows us to define a common type for a group of related values - or, what we call, applicable cases.

A good example is directions: North, South, East, and West

Enumeration can describe ALL possible cases, like days of the week. There are (currently :)) 7, so we will be able to cover all possible values by listing 7 options.

Enumeration can include cases that are only applicable in a particular context, like categories of sellable items in a grocery store: Dairy, MeatProduce, etc. 

Declaring enumerations

The declaration of enumerations is similar to classes. We start with a keyword, which is  enum   (for enumerations), followed by the name we want it to have, and ending with curly brackets. What goes in between those brackets is different though. Instead of properties and methods we simply list cases using keyword  case   for each item:

enum EnumName {
    case caseName1
    case caseName2
    case caseName3
}

Here's a demonstration of implementation using the examples above:

// directions
enum Direction {
    case north
    case south
    case east
    case west
}

// weekdays
enum Weekday {
    case sunday
    case monday
    case tuesday
    case wednesday
    case thursday
    case friday
    case saturday
}

// groceries
enum Groceries {
    case dairy
    case meat
    case produce
}

Utilizing enumerations

To access enumeration cases we also use dot notation: enumeration name, dot, case name:

EnumName.caseName1

Simple enough. But what do we need it for?

One of the most common use cases for enums is with switch , which is a control flow.

Switch compares a given value to possible CASES, and those cases could be defined as enums. Let's implement a switch for directions:

var currentDirection: Direction = .north

switch currentDirection {
    case .north:
        print("Providing directions going North!")
    case .south:
        print("Providing directions going South!")
    case .east:
        print("Providing directions going East!")
    case .west:
        print("Providing directions going West!")
}

Wait, Switch must have a default clause, isn't it required?

We have previously noted that Switch must be exhaustive and must have a default clause. Using enums overrides this requirement. By listing ALL the cases of an enum, we, by definition are covering ALL the possible options, therefore, the default clause is not necessary.

Enumerations have obvious limitations: we can only enumerate items that have finite number of values/cases. For instance, we can count days of the week or letters in an alphabet, but we can’t possibly count all integer numbers, so we can’t create an exhaustive enum for it.

When technical limitation is not an issue, we are still responsible for applying common sense. If we get a number of cases beyond logical comprehension, perhaps, we need to research a more suitable approach. Always ask yourself a question - is there a better way to address 50 (or X) options?

Well, let’s assume we'll only be creating enums that make sense :zorro:.

Enums & Optionals

Remember optionals? An optional is, in fact, an enumeration that covers 2 cases

  • nothing

  • expected type!

The syntax goes like this:

enum Optional<T> {
    case none
    case some(T)
}

Raw values

When we list cases, we are only defining a case name with no particular value.

If we wish to identify cases with specific values, we can assign them to each case. Those are called raw values! All raw values within an enum must be of the same type. The declaration syntax goes like this:

enum EnumName: RawValueType {
    case caseName1 = RawValue1
    case caseName2 = RawValue2
    case caseName3 = RawValue3
}

To access raw values we refer to it as a property of a case:

print(EnumName.caseName1.rawValue) // RawValue1

For example, if we account for a networking condition in our application, there could be a few predictable and quite probable circumstances, like “No network connection” or “No wi-fi" in particular.

In this situation we could use an enumeration to define cases to use in the code as well as the corresponding text to present to the user, like “Oops, seems like we lost connection.”:

enum Error: String {
    case noNetwork = "Oops, seems like we lost connection"
    case noWifi = "No wifi, perhaps it's best to slow down on using your data plan"
}

print(Error.noNetwork.rawValue) // Oops, seems like we lost connection

In this example we assigned strings to our cases but we can use various types. Sometimes they can be set implicitly by providing a value for the first case and letting swift calculate the rest.

For example, what if we want to assign numbers to a literal sequence, like flowers (rose, tulip, camomile)? We can indicate  that "rose" must be associated with 1 and then simply list the rest of the cases. Swift, in  turn, will calculate that tulip needs to be associates with 2 and camomile with 3:

enum Flower: Int {
    case rose = 1
    case tulip
    case camomile
}

print(Flower.rose.rawValue) // 1
print(Flower.tulip.rawValue) // 2
print(Flower.camomile.rawValue) // 3

Enums sound like a useful thing, and very suitable for defining our animal communication experience:

enum CommunicationMethod {
    case bark
    case meow
}

class Animal {
    // properties
    var color = "brown"
    var weight = 20
    
    // methods
    /* common methods */
    
    func communicate(method: CommunicationMethod) {
        switch method {
        case .bark:
            print("I can bark, I must be a dog!")
        case .meow:
            print("I can meow, I must be a cat!")
        }
    }
}

class Dog: Animal {
}

class Cat: Animal {
}

var myDog = Dog()
var myCat = Cat()

// observe results
myDog.communicate(method: .bark) // I can bark, I must be a dog!
myCat.communicate(method: .meow) // I can meow, I must be a cat!

We've added a parameter to our communication function and can now provide a suitable implementation accordingly!

Improving NeXTDestination

Let’s see how handling cases can help improve our project! Here's what we can do:

  • Review the Entertainment classes and subclasses and identify properties that could use enums as a type. Use raw values where you see fit.

  • Define custom enumerations for identified properties and apply them to the project.

Here's a reminder of our latest code:

class Entertainment {
    //properties
    static let generalOptions = Entertainment.generateGeneralOptions()
    var name: String
    var cost: Double
    var location: String
    
    static func generateGeneralOptions() -> [Entertainment] {
        return [Entertainment]()
    }
    
    //initializer
    init(name: String, cost: Double) {
        self.name = name
        self.cost = cost
        location = ""
    }
}

class Activity: Entertainment {
    var weatherConditions = ""
    var difficulty = ""
}

class Arts: Entertainment {
    var genre = ""
    var teaser = ""
}

class Gastronomy: Entertainment {
    var dressCode = ""
    var cuisine = ""
}

Your turn! Try to do it on your own before looking at the results:

// No scrolling yet!

























// enums
enum Difficulty: String {
    case hard = "Hard"
    case intermediate = "Intermediate"
    case easy = "Easy"
}

enum Genre: String {
    case classical = "Classical"
    case modern = "Modern"
    case historical = "Historical"
}

enum DressCode: String {
    case streetwear = "Streewear"
    case casual = "Casual"
    case formal = "Formal"
}

// classes
class Activity: Entertainment {
    var weatherConditions = ""
    var difficulty: Difficulty = .intermediate
}

class Arts: Entertainment {
    var genre: Genre = .modern
    var teaser = ""
}

class Gastronomy: Entertainment {
    var dressCode: DressCode = .casual
    var cuisine = ""
}

Let's review what we've done:

  • Created enums for following classes/properties:

    • Activity difficulty

    • Arts genre

    • Gastronomy dress code

  • Changed the corresponding properties' declaration to assume the new types and values. 

Before we move on, let's make one more improvement. We are now ready to provide a useful collection of entertainment instances to our static variable generalOptions by improving the implementation of the static  generateGeneralOptions() method. Come up with a good number of your own variations - the more the merrier! Here are a few examples:

    static func generateGeneralOptions() -> [Entertainment] {
        var options = [Entertainment]()
        
        // activities
        let activity = Activity(name: "Horseback riding", cost: 60)
        activity.difficulty = .intermediate
        activity.weatherConditions = "No heavy rain"
        options.append(activity)
        
        // arts
        let arts = Arts(name: "MoMA. Items: Is Fashion Modern?", cost: 12)
        arts.genre = .modern
        arts.teaser = "Items: Is Fashion Modern? explores the present, past—and sometimes the future—of 111 items of clothing and accessories that have had a strong impact on the world in the 20th and 21st centuries—and continue to hold currency today. "
        options.append(arts)
        
        // gastronomy
        let gastronomy = Gastronomy(name: "Horseback riding", cost: 60)
        gastronomy.cuisine = "Italian"
        gastronomy.dressCode = .casual
        options.append(gastronomy)
        
        return options
    }

Our program is taking shape nicely :zorro:!

Let's Recap!

  • Enumerations are named data models and allow us to specify cases.

  • The syntax for creating an enumeration is as follows:

    // full declaration
    enum EnumName {
        case caseName1
        case caseName2
        case caseName3
    }
    
    // short declaration
    enum EnumName {
        case caseName1, caseName2, caseName3
    }
  • Accessing cases of enumeration is done using a dot notation:

    EnumName.caseName1
  • Enumerations are frequently used with switches, and optionals are a type of enumeration. 

  • Enumerations can be assigned specific values, called raw values. These values must be of the same type for all cases of an enumeration. The syntax for creating an enumeration with raw values is as follows:

    enum EnumName: Type {
         case caseName1 = rawValue1
         case caseName2 = rawValue2
         case caseName3 = rawValue3
    }

    Raw values can be accessed using the  rawValue   case property.

Exemple de certificat de réussite
Exemple de certificat de réussite