Using Dictionaries and Optional Types in Swift 3.0 (Example)

Suppose you had a class that told you the area of a pizza. What would be nice is for it to tell us the price for a pizza, based on the area of the pizza. We’ll learn the basics of Swift dictionaries and optionals finding our price.

For this lesson, Open a new playground in Xcode, iPad or in a IBM Swift Sandbox called SwiftDictionariesOptionals. Add the following code to the playground:

//Playground version of Pizza
class Pizza{  
    let pi = 3.14
    var pizzaType = "Cheese"
    var  z = 0.0
    var diameter:Double = 0.0{
        didSet{
            z = diameter/2.0
        }
    }
    func pizzaArea() -> Double{
        return pi * z * z // = a
    }
}

This makes a function pizzaArea that calculates the area of a circle from the property z, which is the radius. For some, the declaration of diameter may seem strange. If you are not familiar with computed properties, when we set a diameter, we’ll automatically divide it by two to get a radius and store that value in z. I discusss this more in my lessons on classes, which you can find here.

We’ll put the price per square inch of each type of pizza in a dictionary.  Add this line to the Pizza class, just under the class declaration  for Pizza

let pizzaPricePerInSq = [
    "Cheese": 0.03 ,
    "Sausage": 0.06 ,
    "Pepperoni": 0.05 ,
    "Veggie": 0.04
]

This creates a dictionary. Dictionary entries use the key:value syntax. All keys must be the same type, and must be hashable, which includes all the basic types of Int, String, Double, and Bool. As long as you do not set a value to an enumeration, enumerations can also be used as a key. Types, like everywhere in Swift are implicitly declared.

We use the let declaration in this case. Using let makes this a immutable dictionary. Neither the number of elements or the values of the elements change. Making a mutable dictionary is simple: use var instead of let.

Let’s compute the price of a pizza. Add the following to the Pizza class:

    func pizzaPrice() -> Double{
        return pizzaArea() * pizzaPricePerInSq[pizzaType]
    }

This will give an error
Value of optional type 'Double?' not unwrapped
Any type with a question mark on the end of the type is an optional value. Optional values, besides having a value, has a nil state. Accessing a dictionary returns an optional value, returning nil if there is no key found. These values however are not directly usable. In the simplest form, optional values must be unwrapped to become a value with the ! operator.  The ! operator only works when the optional vale is non-nil.  If the value is nil, it will cause a run-time error.  In our case, using ! is probably safe, but it would be better to replace the pizzaPrice() method with the following code:

 func pizzaPrice() ->Double{
        let unitPrice = pizzaPricePerInSq[pizzaType] 
        if unitPrice != nil {
            return pizzaArea() * unitPrice! 
        } else {
            return 0
        }          
  }

Line 2 finds the Double? value in the dictionary. Line 3 checks if the value is nil. If nil we return a value of 0.0. If not, we multiply the area by the unwrapped unitPrice and return the result.

Display And testing

We are ready to test this function. After the class type the following:

let pizza = Pizza()
pizza.diameter = 10
pizza.pizzaType = "Cheese"
print (pizza.pizzaArea())
print(pizza.pizzaPrice())

You get a pizza area of 7.85 and price of 2.855. Change the pizza type from Cheese to Sausage:

pizza.pizzaType = "Sausage"

The price changes to 4.71.

Now we have a price for every pizza, based on size and ingredients. This was a simple and bit contrived example, but it shows how easy using dictionaries are using Swift. For more on Dictionaries, check out my post How to use Dictionaries in Swift.

Optional Chaining and Guard

The code for pizzaPrice is written in its long form. There is a much shorter form. Comment out the current pizzaPrice method and change it to this:

 func pizzaPrice() -> Double{
        if let unitPrice = pizzaPricePerInSq[pizzaType] {//Optional chaining
            return pizzaArea() * unitPrice
        }
        return 0.0
    }

When we put the assignment of unitPriceas our condition of the if statement we use Optional Chaining. Optional chaining does three things: It unwarps our optional value, assigns it to a constant, and returns a value of false if the optional is nil. So with the one let statement embedded in the if statement, we remove a lot of lines, and don’t have to unwrap unitPrice.

Another way to write the same function is with guard.

func pizzaPrice() -> Double{
        guard let unitPrice = pizzaPricePerInSq[pizzaType]
             else { return 0.0}  
        return pizzaArea() * unitPrice
    }

guard has two differences from if let chaining. The if let construct keeps the variable only within the scope of the true statement block, where guard acts like any other assigned variable in a scope. This is especially useful if you have two or more optionals to unwrap, since you do not have to nest if statements. Secondly, if nil, guard immediately runs code found in its else block to return or break execution. However you can only use guard within a function or loop, so it has some thing that it can  break execution with break or return.

I discuss optional chaining a lot more in my more detailed post on optionals.

The Whole Code

The Playground: SwiftDictionariesOptionals.playground

//: Playground - noun: a place where people can play
// SwiftDictionariesOptionals.playground
// (c)2016 Steven Lipton

import UIKit
class Pizza{
    let pizzaPricePerInSq = [
        "Cheese": 0.03 ,
        "Sausage": 0.06 ,
        "Pepperoni": 0.05 ,
        "Veggie": 0.04
    ]
    let pi = 3.14
    var pizzaType = "Cheese"
    var  z = 0.0
    var diameter:Double = 0.0{
        didSet{
            z = diameter/2.0
        }
    }
   
    
    func pizzaArea() -> Double{
        return pi * z * z // = a
    }
    //: Iteration one of unwrapping
    /*
         func pizzaPrice() ->Double{
            let unitPrice = pizzaPricePerInSq[pizzaType]    //optional type ?Double
            if (unitPrice != nil {                                   //optional type ?Double checking for nil
                return pizzaArea() * unitPrice!             //unwrapping the optional type
            }
            return 0.0
        }
     */
    //: Iteration two of unwrapping -- if let
    /*
    func pizzaPrice() -> Double{
        if let unitPrice = pizzaPricePerInSq[pizzaType] {//Optional chaining
            return pizzaArea() * unitPrice
        }
                return 0.0
        }
    }
     */
    
    //: Iteration three of unwrapping  -- guard
    func pizzaPrice() -> Double{
        guard let unitPrice = pizzaPricePerInSq[pizzaType]
            else { return 0.0}
        return pizzaArea() * unitPrice
    }
}
    let pizza = Pizza()
    pizza.diameter = 10.0
    //pizza.pizzaType = "Cheese"
    pizza.pizzaType = "Sausage"
    print (pizza.pizzaArea())
    print(pizza.pizzaPrice())

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s