Skip to content

Latest commit

 

History

History
575 lines (385 loc) · 15.9 KB

File metadata and controls

575 lines (385 loc) · 15.9 KB

Structs & Enums

Agenda

  • Challenges from last class
  • Structs
  • Break
  • Enums

Challenges from last class

Learning Objectives

  1. Create and use data models with Structs
  2. Add functions to Structs
  3. Use enums and their raw values
  4. Create enums with associated values

Intro

So far, we have learned about different types (Int, String, Array, Optionals).

Almost every time we will need to create our custom types to suit the purpose of our programs. We can create our own types with Classes & Structs in Swift.

<iframe src="https://giphy.com/embed/YiUredNQ5OdxzAaXbV" width="240" height="240" frameBorder="0" class="giphy-embed" allowFullScreen></iframe>

Context for the examples in this lesson: we need to build a program that helps a Boba Tea shop to implement an "order ahead" system, because they constantly have long lines. We need to model how it will work using Swift. Eventually it might become an app.

Struct

A Structure is a type that stores named properties and related behaviors. We can give a struct a name to use later in our code.

struct BobaTea {
  let tea: String
  let sweetness: Int
}

Syntax involves using the struct keyword followed by the name. Everything inside the curly braces will be a property of the struct.

Properties are constants or variables that are a member of the type. Every instance of the struct will have these properties.

We'll start simple. Let's say we have a custom type and we call it BobaTea. Right now the customer can choose the kind of tea they want and the level of sweetness. Every new boba tea will need these two properties.

Your turn: Coffee Struct

  1. Create a new playground in Xcode
  2. Create a struct named Coffee that has three properties:
    1. bean: a String that says what type of bean the coffee uses
    2. sugar: an Int that says how many sugar packets are in the coffee
    3. hasMilk: a Bool that says whether the coffee has milk or not

Creating an instance

var boba = BobaTea(tea: "black", sweetness: 25)
print(boba)
// prints BobaTea(tea: "black", sweetness: 25)

To create an instance, we use the name of the type with the needed parameters. This is an example of an initializer. This one makes sure that all the properties of the struct are set before we try to use them. Safety first!

An initializer is generally a method that the struct can use. But we didn't code it. Swift automatically gives an initializer for structs, using all of its properties.

Your turn: Coffee Instance

Create an instance of your Coffee struct by storing it in a variable named coffee. Make sure to provide values for the bean, sugar, and hasMilk properties.

Accessing Properties

We use dot notation to access the properties in a struct.

let boba = BobaTea(tea: "black", sweetness: 25)
print(boba.tea) // black
print(boba.sweetness) //25

Dot notation will also work to assign values.

If you know you will modify the values in your struct, you should consider making it's properties variables instead of constants. The same applies when considering if the struct should be a constant or variable.

Challenge 1

Include the type BobaTea from the example in your playground.

Write a structure called Order that will represent a client's order. It needs two properties:

  • name of type String
  • boba of type BobaTea 😯

Create-A-Boba

We can use functions to create an instance of a struct and its properties. For example, let's write the createBoba function that takes teaType and sweetnessLevel as inputs, and returns a new BobaTea:

func createBoba(teaType: String, sweetness: Int) -> BobaTea{
    let boba = BobaTea(teaType: teaType, sweetnessLevel: sweetness)
    return boba
}

Your turn: createCoffee

Write a createCoffee function that takes beanType, sugarLevel, and containsMilk as inputs (String, Int, Bool respectively), and returns a Coffee struct based on those input parameters.

Challenge 2

  1. Write a function that given input parameters returns an order type Order (You'll need to make an Order struct!)
  2. Then create an order using the function (this should return an Order)
  3. Then print it's details.

This is how I should be able to call your function: let newOrder = createOrder(withTea: "black", sweetness: 25, forCustomer: "Adriana", includeBoba: true)

and this is what you'll print in the end: Adriana ordered black boba tea, 25% sweetness, with boba

Methods

We know now that structures can have constants and variables. They can also have their own functions. And every instance will have access to them, to execute them.

Functions that are members of types are called methods.

What if we want to able to display the complete order of a customer every time? It might be useful to include a method inside the Order struct.

Challenge 3

struct Order {
  var boba: BobaTea
  let name: String

  func printDescription(){
    print("\(name) ordered \(boba.tea) boba tea, \(boba.sweetness)% sweetness, \(boba.hasBoba ? "with boba" : "no boba")")
  }
}

let newOrder = createOrder(withTea: "black", sweetness: 25, forCustomer: "Adriana", includeBoba: true)

newOrder.printDescription()
//Adriana ordered black boba tea, 25% sweetness, with boba

Do the same improvement in your implementation, then move your createCoffee function into the Coffee struct.

Structures as value types

A value type is a type whose instances are copied when assigned.

var boba = BobaTea(tea: "black", sweetness: 25, hasBoba: true)
var anotherBoba = boba
print(boba.tea)         // black
print(anotherBoba.tea)  // black

boba.tea = "oolong"
print(boba.tea)         // oolong
print(anotherBoba.tea)  // black
This shows that when boba is assigned to anotherBoba, it's copying the value into the new variable. They are two independent instances, with different memory addresses.

Exploring Swift types

Many of the standard Swift types are structures:

  • String
  • Double
  • Bool
  • Array
  • Dictionary

You can confirm this by looking at the documentation 🤓 (command+click -> jump to definition)

Questions

Q1: When is it useful to define a struct?

Q2: Which of the following is NOT a key component of a struct?

  1. Name
  2. Properties
  3. Functions
  4. Enumerations

Q3: What do you call a function that's added to a struct?

[10m] BREAK

Enums

An enumeration is a list of related values that define a common type. It's a great way to work with type-safe values.

Enums can also have methods and properties, that makes them extra powerful.

enum TeaType{ case black case oolong case lavender case chai } // Creating an instance var typeOfTea = TeaType.chai

Standard practice: start each case with lowercase.

Your turn: Coffee Enum

Make a CoffeeType enum, where robusta, liberica, and arabica are valid types.

Switch statement

var typeOfTea = TeaType.chai

switch typeOfTea {
case .black:
    print("This is black tea.")
case .oolong:
    print("This is oolong tea.")
case .lavender:
    break
case .chai:
    print("This is chai tea.")
}

Switch statements are another option to handle control flow. A switch will execute different code depending on the value of the variable given.

A few notes about switch statements

  • They need to be exhaustive (check all cases or use default)
  • If we want nothing to happen in a case, we write break
  • Cases can't be empty
  • They work with any data type
  • You can group several cases
switch typeOfTea {
case .black, .chai:
    print("This is a house favorite.")
case .oolong, .lavender:
    print("This is a seasonal special.")
}

Your turn: Coffee Case

Write a typeOfCoffee switch that prints out the name of the coffee type.

Raw values

We can assign a value to each case in an enum. This helps handling each case or getting a value to work with in our program.

var typeOfTea = TeaType.chai

enum TeaType : String{
    case black = "black"
    case oolong = "oolong"
    case lavender = "lavender"
    case chai = "chai"
}
print(typeOfTea.rawValue) // chai

enum TeaType : Int{
    case black
    case oolong
    case lavender
    case chai
}

print(typeOfTea.rawValue) // 3
When we add raw values we need to specify their type right after the name of the enum. If we use Int, and don't specify otherwise, it will enumerate the cases starting with 0.

We can use raw values to instantiate an enum.

let teaType = TeaType.init(rawValue: 2)
if let tea = teaType{
    print(tea)
}

The initializer will give back an optional type. Since there's no guarantee that the raw value we're giving matches one of the available types of tea.

Associated values

  • Enums can have 0 or more associated values.
  • These values need their own type.
  • We can give them names, like external names in functions.
  • An enum can have either raw or associated values, not both
enum OrderFullfilment {
  case success(message: String)
  case error(message: String)
}

We will handle the order result with an enum. The associated value will be a message, that we can set later when using it.

func makeOrder(order: Order) -> OrderFullfilment {
    let date = Date()
    let calendar = Calendar.current
    let hour = calendar.component(.hour, from: date)
    print(hour)
    if hour < 17 && hour > 9{
        return .success(message: "You can pick up your order in 30 min")
    }else{
        return .error(message: "We are closed, try tomorrow")
    }
}
let orderResult = makeOrder(order: newOrder)

switch orderResult {
case .success(let message):
  print("Order result: \(message)")
case .error(let message):
  print("Order result: \(message)")
}

We used let bindings to read the associated values.

Challenge 5

Finish the implementation of the Boba Tea shop.

  • Use enums to represent the tea types
  • Add an option to customize milk too: whole, almond, oat
  • Include the makeOrder function to practice using associated values
  • Experiment, break things 🤓

Find the solutions to each step of the challenges here.

Questions

Q1: Which of the following would be best represented with an enumeration?

  1. Names of people in a room
  2. Political parties
  3. Addresses
  4. Compass degrees

Q2: Which of the following would be best represented with an enumeration? Choose all that apply

  1. Hair colors
  2. T-shirt sizes
  3. Favorite numbers
  4. Car speeds

Q3: Which of the following would be best represented with an enumeration? Choose all that apply

  1. Car manufacturers
  2. Wi-fi network names
  3. Professional basketball teams
  4. Shoe brands

Lab + After Class

  • Lab 4
  • Optional Review on Swift topics (up to what we've seen) Swift Playgrounds
  • Look up Property Observers
  • Check the documentation for Optionals and discover how they relate to today's lesson. Optional chaining

Additional Resources