Tag Archives: swift tutorial

Swift Swift Tutorials: Calculator Style buttons in an Auto Layout Xib

frame 2015-01-21 17.18.00Some people want calculator-type buttons. It would be nice to have such buttons for say, an order entry app. In this lesson we’ll put together a set of buttons to make a layout for an order entry system. Along the way I’ll show how to use a Xib as a modal view in Swift. This requires some knowledge of auto layout. If you haven’t used Auto layout before I’d suggest reviewing Beginning Auto Layout first.

Set Up the Starting View

Start by making a new project with Command-Shift-N in Xcode. Make a single view project and name it PieOrderEntry, make the device Universal. Choose Swift for the language. Set your storyboard up like this with one label and one button.

Screenshot 2015-01-22 14.32.50

Click the label. In the properties inspector center align the text. Then using auto layout, pin the label 50 up, 30 left and 30 right. Update the frame with new constraints.

Screenshot 2015-01-22 14.34.59

Then pin the Order Pie button 30 up, 30 left and 30 right, again updating the frames.

Screenshot 2015-01-22 14.35.45

Go into assistant editor, select Automatic, and click the view controller in the storyboard. Control-drag the label to the view controller. Name the outlet statusLabel . Control-drag the button and make it a target action named myModalButton . Remove the rest of the methods in the view controller class so your view controller looks like this:

class ViewController: UIViewController {
    @IBOutlet weak var statusLabel: UILabel!
//MARK: - Target actions
    @IBAction func myModalButton(sender: UIButton) {
    }
  //MARK: - Delegate methods
}

Make a View Controller With a Xib

There is only one check box between using storyboard and a xib. Press Command-N to get a new file, and under iOS click a new Cocoa Touch Class . Click Next and fill out the dialog box like this with a new subclass of UIViewController named MyModalVC:

Screenshot 2015-01-22 14.43.08

Be sure to check on the Also create XIB file selection. This will create and associate a xib file with this view controller. If you have never seen a xib, it looks a lot like a storyboard with only one scene. You use it exactly the same way — even auto layout. The difference is the Xib is independent of the storyboard. You can take the MyModalVC.swift and MyModalVC.xib file from here, and import them into another project ready for use.

Layout for the Xib

After making the view controller, the xib should appear.

Once loaded, switch to the Storyboard. Add one label and seven buttons. In the properties inspector, make the label text read What is my pie? and center align the text. Position it towards the top of the view.  On one button, label it Submit Result with a red background and white text. Drag the Submit Result button towards the bottom. Make the rest of the buttons a blue color with white lettering. Title the buttons Pizza,Chicken Pot,Shepherd’s,Apple,Chocolate,and Blueberry. Arrange the buttons roughly like this:

Screenshot 2015-01-21 05.12.07

We will use auto layout pins to do most of the work. We need to get the bottom and top constrained first to always be the nearest neighbor.
Select the label. In the properties inspector,change the background to white. Pin the label 50 from the top and 0 from left and right. Update the frame.
Change the view’s background to black. It will help us see everything moving around.

Screenshot 2015-01-21 05.38.31

For the button titled Submit Result, Pin it 20 from the bottom and 0 from the left and right. Update the frame.

We now can start working on the buttons. Like Chapter 3, we will start with one button, and pin everything around it together. Because everything will get messy we will not update frames until we are done with this process.

Close the left hand navigator frame and open the assistant editor to the preview mode. The default iPhone 4 inch will do fine for checking our work as we go. Your workspace should look like this:

Screenshot 2015-01-21 05.47.02

We will use a 20 point gutter between buttons. Select the Pizza button in the upper left. Pin the button 20 up, 0 left, 20 down, and 20 right.
Select the Apple button. Since we already have a relationship to the Pizza button, we don’t need a top pin. Pin the Apple button 0 left, 20 right, and 20 down.
Select the Chocolate button. Pin the button down 20 and right 20.
Select the Blueberry button. Pin the button down 20 and right 0.
Select the Shepherd’s button. Pin the button up 20, left 20, right 0, and down 20
Select the Chicken Pot button. Pin up 20 and down 20.
You now have all the buttons pinned in all four directions. Clicking on each button and make sure you see four constraints, which most likely will be misplaced like this.

Screenshot 2015-01-21 06.02.17

If you have a constraint missing, add it from the pin menu. The Preview has the buttons in the right place but sized incorrectly:

Screenshot 2015-01-21 06.06.58

We have not set any size information. Select the Pizza Button. Control-Drag from the Pizza Button to the Label. Select Equal Heights.
Control drag from the Pizza Button to the Submit Result button. Select Equal Heights.
Control-Drag from the Pizza button to the Apple Button. Shift Select Equal Width and Equal Heights and press return. Repeat for the other four blue buttons, always dragging from Pizza to the Chocolate, Blueberry,Meat and Chicken Pot.

In the resolver, for All Views in View, select Update Frames.

Screenshot 2015-01-21 06.16.39

The label and Submit Result button is a bit too big. Select the label. Go to the Size inspector and select Edit for the Equal Height to Pizza. Change the multiplier to 2:3.

Screenshot 2015-01-21 06.21.00

Select the Submit Result Button and change the multiplier to 1:2

Screenshot 2015-01-21 06.23.26

Again for all views in view, select Update Frames. We have a completed layout that looks pretty good.

Screenshot 2015-01-21 06.28.38

Resolving Six Warnings

Unfortunately, we get six warnings. Going to the error panel you will find them:

Screenshot 2015-01-21 06.41.24

Reading the error we note that each button is off by one. Annoyingly you can resolve all but the pizza. If your resolve the pizza misplacement, all the errors come back.
All of the errors are height errors. This is a rounding error, and its cause is the multiplier for the label which is 2:3. As a repeating decimal, it is causing rounding errors between all the views. One easiest answer is to change the multiplier from 2:3 to 1:2. The error goes away.
In the preview, both Landscape and Portrait work:

Screenshot 2015-01-21 06.52.39  Screenshot 2015-01-21 06.52.51

Connect the Labels and Buttons

Set your workspace up for wiring up the views. Open the assistant editor if not already open. Select Automatic for the assistant editor. Hide the Properties or Size Inspector to give yourself some room.

In the assistant editor, remove the methods already in the class, including the commented out prepareForsegue . Control drag the What is my Pie Label to make an @IBOutlet named pieLabel . Control drag the Submit Result button and make an @IBAction for a UIButton named submitResult . Control drag the Pizza button and make an action for a UIButton named pieSelectionButton . Your class should look like this:

class MyModalVC: UIViewController {
  @IBOutlet weak var pieLabel: UILabel!

  @IBAction func pieSelectionButton(sender: UIButton) {
  }

  @IBAction func submitResult(sender: UIButton) {
  }
}

From the circle to the left of the pieSelectionButton method, click then drag to the other five blue pie buttons and release to connect them to the action. When connected, they will highlight as you hover over the circle.
Add the following property above the @IBOutlet:

var pieString = "No Pie"

Fill in our two methods so the entire class looks like this.

import UIKit
class MyModalVC: UIViewController {
   var pieString = "No Pie"
   @IBOutlet weak var pieLabel: UILabel!

    @IBAction func pieSelectionButton(sender: UIButton) {
        pieString = sender.titleLabel!.text!						//7
        pieLabel.text = pieString + " Pie"							//8
    }

    @IBAction func submitResult(sender: UIButton) {
        dismissViewControllerAnimated(true, completion: nil)		//12
    }
}

Line 7 takes the tile and places it in a string property pieString. Note the titleLabel property is an optional UILabel and the text property of UILabel is also optional. We unwrap them before concatenating to the string” Pie” in line 8. Line 12 dismisses the modal view, though we will change this shortly.
Open up the navigator. and close the assistant editor. Go back to the viewController.swift file. Just under the class declaration, add the following:

let pieVC = MyModalVC(nibName: "MyModalVC", bundle: nil)

There is an initializer for UIVIewController called init(nibName:bundle:) . This line creates an instance of the view controller MyModalVC that uses the xib specified as a string for nibName: We can specify a bundle location if necessary. If we leave the bundle nil , the compiler will search for it in our code bundle.
We need to set a transition and present the modal view controller. Change the code for myModalButton() to the following

  @IBAction func myModalButton(sender: UIButton) {
    pieVC.modalTransitionStyle = UIModalTransitionStyle.PartialCurl
    presentViewController(pieVC, animated: true, completion: nil)
  }

In line 2, We set the transition style to .PartialCurl to simulate a waiter’s order pad. We then present the view controller in line 3. This is enough code to run the app. Build and Run.

      frame 2015-01-21 17.18.00      frame 2015-01-21 17.18.30

Add a Delegate to the Modal View

The modal view currently does not give the root view controller any data back. For that, we will need a delegate. Delegates in modal views are a bit simpler than in navigation controllers. We still start with a protocol. Go to the MyModalVC.swift code and add this above the class declaration:

protocol MyModalDelegate{
  func myModalDidFinish(controller:MyModalVC, pie:String)
}

In the MyModalVC class add a delegate property:

var delegate:MyModalDelegate! = nil
Change the method submitResult() to use the delegate:
@IBAction func submitResult(sender: UIButton) {
    //dismissViewControllerAnimated(true, completion: nil)
    delegate.myModalDidFinish(self, pie: pieString)
}

Now head over to ViewController.swift to adopt the protocol:

class ViewController: UIViewController,MyModalDelegate {

Right on cue, Xcode complains we have not implemented the delegate. Add the following code to the ViewController class:

//MARK: - Delegate methods
func myModalDidFinish(controller: MyModalVC, pie: String) {
    statusLabel.text = “Order ” + pie + " pie"
    controller.dismissViewControllerAnimated(true, completion: nil)
}

We put our result in the label, then dismiss the modal view from the delegate method. Now assign the delegate to this view controller by changing myModalButton :

@IBAction func myModalButton(sender: UIButton) {
    pieVC.delegate = self
    pieVC.modalTransitionStyle = UIModalTransitionStyle.PartialCurl
    presentViewController(pieVC, animated: true, completion: nil)
}

While storyboard-based views need to place the delegate declaration in a prepareForSegue method, here we directly send it to the view controller. We now should have a working delegate. Build and run

frame 2015-01-21 17.19.00      frame 2015-01-21 17.19.30

And it works!

The Whole Code

My ModalVC.swift

//
//  MyModalVC.swift
//
//
//  Created by Steven Lipton on 1/20/15.
//  Copyright (c) 2015 MakeAppPie.Com. All rights reserved.
//

import UIKit
protocol MyModalDelegate{
    func myModalDidFinish(controller:MyModalVC, pie:String)
}
class MyModalVC: UIViewController {
    var pieString = "No Pie"
    var delegate:MyModalDelegate! = nil
    @IBOutlet weak var pieLabel: UILabel!

    @IBAction func pieSelectionButton(sender: UIButton) {
        pieString = sender.titleLabel!.text!						//7
        pieLabel.text = pieString + " Pie"							//8
    }
    @IBAction func submitResult(sender: UIButton) {
        //dismissViewControllerAnimated(true, completion: nil)		//12
        delegate.myModalDidFinish(self, pie: pieString)
    }

}

ViewController.swift

//
//  ViewController.swift
//
//
//  Created by Steven Lipton on 1/20/15.
//  Copyright (c) 2015 MakeAppPie.Com. All rights reserved.
//

import UIKit

class ViewController: UIViewController,MyModalDelegate {
    let pieVC = MyModalVC(nibName: "MyModalVC", bundle: nil)
    @IBOutlet weak var statusLabel: UILabel!

    @IBAction func myModalButton(sender: UIButton) {
        pieVC.delegate = self
        pieVC.modalTransitionStyle = UIModalTransitionStyle.PartialCurl
        presentViewController(pieVC, animated: true, completion: nil)
    }

    //MARK - Delegate Methods
   func myModalDidFinish(controller: MyModalVC, pie: String) {
        statusLabel.text = "Order " + pie + " pie"
        controller.dismissViewControllerAnimated(true, completion: nil)
   }
}

Kinderswift 8 Challenge Answers

Watch the video for any commentary. Here are two answers. There a lot of possible ones.

Answer 1

func blueColor(blueVal:Int) -> UIColor {
let color = CGFloat(CGFloat(blueVal) / 255.0)
return UIColor(red: 0.0, green: 0.0, blue: color, alpha: 1.0)
}


blueColor(255)
blueColor(23)
blueColor(128)

Answer 2


//necessary code to run the answer
func pizzaArea(z:Double) ->Double {
//area of pizza : pi * z * z = a
    let pi = 3.14159265358
    return pi * z * z
}


func deepDishVolume(diameter:Double,height:Double){
    let radius = diameter * 0.5
    let area = pizzaArea(radius)
    let volume = area * height
    println(radius,area,height)
}

deepDishVolume(13, 2)


KinderSwift Episode 3: Variable Assignment in the Xcode Playground

In this episode of our beginner’s series on Swift, we will dive into programming with the most fundamental part: assignment of values to identifiers. We’ll use the new Xcode playground feature to explore var and let. We’ll learn how to add numbers and a few tips about common syntax errors and the Xcode’s autocomplete feature

Edited Transcript

Welcome back to episode 3 of KinderSwift. In this lesson we’ll begin using the playground to learn to code.

Open Xcode. If you have any other projects open, close them. and if necessary restart Xcode.

If you don’t see the welcome page hit Shift-command-1 on the keyboard.

kinderSwift 3 open frame welcome

When you have a welcome page, Click Get started with a playground.
kinderSwift 3 click playground

A new window asks you about the playground. Name the playground MyKinderswiftPlayground, and make sure the drop down says iOS.

kinderSwift 3 open playground

Click Next and save it in the same place we saved the project from last time, which should be the default setting.Click Create, and we are in the playground.
frame1shot

 

The playground is an interactive environment that lets you see what is happening immediately. In this early part of your coding experience, it will be a lot easier and faster to use the playground than coding an app.

Delete the line right under import UIKit. Leave the UIKit line there. That is what makes our code work.

The first thing I’d like to teach you is this

//this is a comment

That is, as it says, a comment. Swift ignores it, but it is a great way for us to put notes to ourselves and others about what we are doing in our code. Now type this:

let myNumber = 4

This assigns the integer 4 to the identifier constant myNumber.
Now what does that mean? Let’s break this down piece by piece.

We have the keyword let. Keywords are words in a computer language which the system knows is a command. let is a command to make a constant, which is a value that will always stay the same.

kinderSwift 3 let description

After the let we have a space. Spaces in programming languages are as important as in written languages because they tell us we are going to the next word.
We then have the identifier myNumber, which is a way of naming a value. In Swift we write identifiers usually like this where we squish words together without spaces and capitalize where we are supposed to have a space.

Constants and variables like this will start with a lower case letter, to distinguish it from the types and classes we’ll learn in a few sections.

The equals sign acts as the assignment operator, another keyword that says take the value following this and make it the value of the identifier before it.

kinderSwift 3 equals description

What that means is myNumber is 4, and I can’t change it, because it is a constant. if you look to the right column in the playground it shows the value of myNumber.

frame04shot

Lets add another constant type this:

//same thinglet
myOtherNumber = 2

Now we made another constant with the value 2. Again to the right is the value of the number.

Besides let there is also var, which makes a variable. A variable is an identifier which can change values. So let’s type:

//variable
var myResult =  0

The result is 0. Once you have assigned an identifier with var or let, you use the Identifier without them.

If we wanted to add two numbers together we could do this:

myResult = 4 + 4

The result shows up as 8. Every time you assign a value to myResult it changes to that value. You can also use identifiers here. for example:

myResult = myOtherNumber + 4

The result shows up as 6. When you assign some value to myResult it change to that number. You can also use identifiers here. For example, type this:

myResult =  4 + myOtherNumber

You’ll notice the little drop-down box that appears as you type. This tells you identifiers and keywords that Xcode knows. These are auto-completion shortcuts to typing

kinderSwift 3 auto-complete

You can use your arrow keys then the tab keys to select the value. If there is only one value you just hit tab. You can also use your mouse to click on them. You can also keep typing. The choices will refine themselves. Press tab at any time.

You use variables just like constants. You can even use the variable you are assigning and assign it back to itself. For example, type this:

myResult = myResult + myNumber

This Shows us a result of 10. myResult was 6, and we added 4 more to it, then saved that value back into myResult.
That is how we use a variable. We assign it either literal values like number or we assign an expression to it like adding two numbers together. You cannot do this to a constant. try this:

myNumber = 4+4

You will get a little red error circle. The red circle means there is a mistake in the syntax of this line of the program. Click on the error and you will see the error message.

kinderSwift 3 let error

Xcode is telling you that anything assigned by let cannot be changed for the life of the application. You are stuck with it.
Change it to the variable myResult and the error goes away.

You will get an red error for misspelling an identifier. Make myResult myresult and see what happens. Swift like many computer languages is case sensitive. Be sure you name things correctly.
That’s it for this lesson, Next we’ll go on to doing a little more math.

The Whole Code

//This is a comment
let myNumber = 4
//Same Thing
let myOtherNumber = 2
//variable
var myResult = 0
myResult = 4 + 4
myResult = myOtherNumber + 4
myResult = myNumber + myOtherNumber
myResult = myResult + myNumber
myResult = 4 + 4

The Swift Swift Tutorial: Using Tuples and Creating Multiple-Return Functions

A pizza tuple
A pizza tuple

While not classified as a collection type by Apple, tuples are the new collection-like type found in Swift. Tuples fit in a strange place between structs and arrays, but allow for some rather remarkable flexibility in code — particularly when returning more than one value in a function.

While an array is a collection type containing elements of the same type, A tuple in swift is a collection containing values of varying types. This does not make them a substitute for arrays, but a temporary way of moving several values around at the same time.

Declaring and Accessing Tuples

At its simplest, you declare a tuple like this:

 let myPizzaTuple = ("Green Pepper", 18.0)
 

Alternatively, you can add names for the parts of the tuple.

 var myNamedPizzaTuple = (topping:"Mushroom",size:10.0, crust:"Pan")
 

Getting values from a tuple has a few variations . One is to do the reverse of assigning the tuple, creating two variables with the data of the tuple, like this:

 var (topping,size) = myPizzaTuple
 println("Your pizza is a \(size) inch \(topping)")
 

You can also use an index starting at 0 to access the elements of the tuple. This index uses dot notation, not a true index like an array.

println("Your pizza is a \(myPizzaTuple.1) inch \(myPizzaTuple.0)")

Outside of the examples here, I personally cannot see a use for this type of access. You can’t enumerate with variables like an array and it makes code difficult to read. My preferred practice is to use the named tuple, like I did with myNamedPizza:

println("Your pizza is a \(myNamedPizzaTuple.size) inch \(myNamedPizzaTuple.crust) \(myNamedPizzaTuple.topping)")

Consistent with all types in Swift, a constant tuple declared by let is immutable. A tuple declared by var allows changing of the values within the tuple, In either case, there is no mechanism to change the number of elements in the tuple.

myNamedPizzaTuple.topping = "Pear Gorgonzola" //works fine
myPizzaTuple.0 = "BBQ Chicken" //gives error 'Cannot assign to 0'

You can initialize a variable with a constant tuple, and then change the variable. The tuple is not affected.

topping = "Wasabi Siracha Tuna"
println("Toppings are \(topping) pizza and a \(myPizzaTuple.0) pizza")

The code above will print the following:
Toppings are Wasabi Siracha Tuna pizza and a Green Pepper pizza
If the  assignment affected the tuple , we would have two mouth burning pizzas, not just one.

The cases above change one element of the tuple’s values. You can change all values in the tuple like this if declared as var:

myNamedPizzaTuple = (topping:"Pear Gorgonzaola",size:10.0, crust:"Thin")

Using a Tuple

While at first glance tuples look like arrays, they act a lot more like structs. That is how we use them. Let’s look at a parallel example struct. Core graphics has the struct CGPoint:

struct CGPoint {
CGFloat x;
CGFloat y;
};
typedef struct CGPoint CGPoint;

If we had a case we didn’t need core graphics but needed coordinate data I could declare this

var aPoint = (0.0,0.0) //tuples of Double
var anotherPoint = (x:CGFloat(0.0),y:CGFloat(0.0)) //tuples of CGFloat

Line 1 is a simple version which makes the coordinates (x,y) as doubles. We cannot declare type in a tuple, so often we will have to cast or initialize a value in the correct type. In line 2, if we want some compatibility with Core graphics, we initialize (x,y) with CGFloat and name them.

If I name my tuple, like I did in anotherPoint I can use tuples like a struct:

anotherPoint.x = 23.4
anotherPoint.y = 16.1

Since we can get access to all the variables in a tuple at the same time, we can also do this:

aPoint = (10.1,12.3)
anotherPoint = (x:10.1,y:12.3)

Suppose I needed polar coordinates, ones  based on the radius and angle in degrees, but I only need to use this for a few calculations. Instead of spending time making a struct, I can declare it as a tuple in the function or method.

var radialPoint = (radius:10,angle:0)

Then use it in code somewhere. Suppose I have a method which places pepperoni slices at a polar coordinate on a pizza. Here is a code fragment to place the pepperoni at 30 degree increments from each other in a circle.

angleIncrement = 30
while radialPoint.angle < 360.0{
    placePepperoni(distanceFromCenter:radialPoint.radius, rotation:radialPoint.angle)
    radialPoint = ( 10, radialpoint.angle + angleIncrement)
}

This example could be done without a tuple of course, but note that in related values, such as coordinates, it makes for better documentation of code. This will also come in handy in handling the key and value pairs of dictionaries, which we will discuss in more detail next time when we discuss dictionaries.

Returning Multiple Values

Suppose I needed a converter from a CGPoint  value to polar values. I would need to return two values instead of the usual one. I could just set up a struct somewhere else in my code, then assign return to that cutom type, but there are times I’m only going to be needing that data once or twice. Assigning it in several other places gets cumbersome. This is the biggest reason swift has tuples: We can return tuples, and when we do, we return multiple values. For example, look at this fragment of code:

 func cartesianToPolar(point:CGPoint)->;(radius:CGFloat,angle:CGFloat){
 /* do some math here to get radius and angle */
     return (radius,angle)
 }
 

I’ll leave the math to those who like trignometry, but for the example there are some conversion formulas to return a polar coordinate from a CGPoint. The polar coordinate has two values not just the one we usually have. I dedeclare what the tuple will look like in the return part of line one and assign the values in the return statement in line 3.  Getting the values out of the function might look like this:

let polarPoint = cartesianToPolar(CGPointMake(5.0, 5.0))
println( "The polar point is\(polarPoint.radius) for the radius and \(polarPoint.angle) for the angle")

Which is pretty easy to deal with.

Tuples can be confused with a lot of other types, like arrays and structs. They are not a full collection types, nor are they full structs. They are a way of making temporary struct-like objects. Their main purpose is to return multiple values with multiple types from a function. In that they excel, and provide developers a new way to write functions.

The Swift Swift Tutorial: Ten Points for Using Optionals.

Screenshot 2014-07-30 12.57.44In the first month and a half of using Swift, I could argue the optional value is the most difficult new concept. It isn’t hard, just something different to wrap one’s head around. That Apple’s documentation peppers information about optionals in two different e-books without consolidating the information doesn’t help. I decided to combine what I know about optionals into one post as a reference.

[edited for Swift 1.2 5/1/15]

1. You need nil so you need optionals

There are many cases where we need a state that says “this does not exist yet.” When I am deciding on breakfast for example I’m thinking about yogurt, coffee and chocolate croissants. I’m not thinking about Pizza. My choice for a pizza for lunch is not even on my mind. My choice for pizzaType is nil. The same is true in code. There are places where things just don’t exist yet, and we need a way to show it. That is what optionals are for. Optionals change any type into a type with a flag for the nil state and a value. If the optional is nil, there is no value, if the optional is non-nil there is a value.
we declare an optional like this:

var pizza:Pizza?= Pizza()

This would make an optional from the Pizza class. Swift can also do this:

var numberOfPizzas:Int? = nil

This would set an integer optional to nil. Optionals are a lot like pointers in Objective-C. However in Objective-C you can’t put a pointer to an integer, only classes. In Swift any type, not just classes, can be an optional.
When declaring optionals, if starting with nil, you can leave off the nil assignment if you wish.

var numberOfPizzas:Int?

2. Unwrap before use

Assigning to an optional is just like assigning to anything else.

numberOfPizzas = 5

However getting data from an optional is slightly more challenging. As I mentioned earlier, optional values have two parts, one for nil and one for the value. If we retrieve a value from numberOfPizzas like this:

var myNumberOfPizzas:Int =  numberOfPizzas

The result is a compiler error:

Value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?

Here we need to use the force unwrap operator ! to get an integer value out of the optional

 var myNumberOfPizzas:Int =  numberOfPizzas!

The compiler error message now disappears.

3. Always check for nil

If we run now while myNumberOfPizzas is nil, we get a run time error

fatal error: Can't unwrap Optional.None

We cannot unwrap an optional that is nil. In order to prevent this, first check an optional for nil, then get its value using the force unwrap operator !

var myNumberOfPizzas:Int =  0
if numberOfPizzas != nil{
    myNumberOfPizzas = numberOfPizzas!
}

Do this for anything you make an optional. Also check a class that is optional before using the methods and properties of the class.

4. Don’t check for nil to break your code

There are times you are absolutely certain there will be a non-nil optional. If that optional is nil, something is very wrong. This is the big exception to rule #3. You want to crash the code right here, and start debugging to find out why that optional is nil. For example, when working with delegates you will have some statement that call the delegate method,

    @IBAction func saveColor(sender : UIBarButtonItem) {
            delegate!.myVCDidFinish(self, text: colorLabel!.text!)
        }

If the delegate was not set, this will crash, letting you know to go set the delegate property.

5. Implicit optionals are still optional

Although  rule 4  is an exception to  rule 3 to check for nil, it ends up there are several times we know an optional is non-nil (rule 6).  For ease of use, Swift allows you to declare a implicitly unwrapped optional. For example:

var numberOfPizzas:Int! = 1

Makes a implicitly unwrapped optional with a value of 1. Implicitly unwrapped optionals aren’t force unwrapped. Use them the same way as non-optionals, and don’t force unwrap when accessed.

myNumberOfPizzas = numberOfPizzas

In code they will look like non-optionals. It’s easy to forget that they are optionals. They are, and as optionals if they ever go nil, you will get the run time error

fatal error: Can't unwrap Optional.None

from something that doesn’t look like an optional. Just a word of caution to watch for this, especially in light of rule 6

6. Optionals are not optional for API’s

Objective C classes, as pointers, expect a nil value. As almost everything in the API’s and frameworks is an Objective-C class, almost everything you will work with is an optional. The compiler will remind you of this very often when you try to assign something to a non-optional value that is an optional value. As you check help screens and documentation you will see definitions of API like this:

func prepareForSegue(_ segue: UIStoryboardSegue!,sender sender: AnyObject!)

The definition tells you segue is an implicitly unwrapped UIStoryboardSegue and sender is a implicitly unwrapped AnyObject. For the most part, optionals in the API are implicitly unwrapped, so rule 5 above applies often when working with frameworks and API’s. Read these definitions carefully. For example, while in Swift alone you can switch around NSString and String rather easily, many places that an NSString is a property in Objective-C requires String! in Swift. For example in UILabel, the text property’s definition is:

var text: String!

The compiler will give you errors if you forget this in some situations. In some it will not…until it is too late.

7. Cast optionals down from AnyObject

What Objective-C returns as a type id, Swift returns as AnyObject!. While AnyObject! may be nice,  if you are trying to get some properties or methods from your returned object, AnyObject doesn’t work so well. Often, there is an expected subclass for the return value, such as UIButton. For example

var myButton = UIButton.buttonWithType(UIButtonType.System)

Gives the compiler warning:
Variable 'myButton' inferred to have type 'AnyObject!', which may be unexpected
If you try to use a method of UIButton, Xcode may not find any methods. Downcasting to UIButton using as! or as?

var myButton = UIButton.buttonWithType(UIButtonType.System) as! UIButton

Silences the warning, creating a non-optional UIButton. To keep consistent with Objective-c code, this is more accurate:

var myButton:UIButton! = UIButton.buttonWithType(UIButtonType.System) as? UIButton

This code produces a implicitly unwrapped optional UIButton. The as? makes myButton an optional, and the :UIButton! makes it implicitly unwrapped.

8. Chain methods not properties

Another way to unwrap an optional is to use optional chaining. For example, if  UIButton above was:

var myButton: UIButton? = UIButton.buttonWithType(UIButtonType.System) as? UIButton

It would be a basic-flavor optional. I could try this:

myButton.setTitle("Pizza", forState: UIControlState.Normal)

I would get the following error message:
'UIButton?' does not have a member named ‘setTitle'

I could force-unwrap it like this:

myButton!.setTitle("Pizza", forState: UIControlState.Normal)

If it is nil, I would get a run-time error. Another way to unwrap and check if it is nil is optional chaining like this:

myButton?.setTitle("Pizza", forState: UIControlState.Normal)

Instead of a ! we use ? The expression, if nil, does nothing. If non-nil, it calls the method. This works great for methods, but don’t use it for assigning properties of an optional class. For example, this

myButton?.tintColor = UIColor.redColor()

gives you the error:

Cannot assign to the result of this expression

You must force unwrap the value to assign it, with the risk of a run time error unless you check it. This would be best:

if myButton != nil{
myButton!.tintColor = UIColor.redColor()
}

9. Outlets are weak, weak is optional

[Edited for Xcode 6 Beta 4]
Some might argue that rule #4 applies to the last example. If myButton was strong, that would be true. However If using outlets, anything that is an outlet is weak. Prior to Xcode 6 Beta 4 If you have in your code

@IBOutlet var myButton:UIButton

and Xcode 6 beta 4 and later if you have:

@IBOutlet var myButton:UIButton!

Xcode replaces it behind the scenes with

@IBOutlet weak var myButton:UIButton! = nil

All outlets are implicitly weak optionals. Weak references are those that can be destroyed by ARC to prevent reference cycles. Weak references need to have a state of nil for when they do not have a reference so ARC can dispose of them. Thus weak references must be optionals. The weak keyword only works on optionals and classes. If you try this:

weak var myNumber = 1

The compiler will complain with a error.

'weak' cannot be applied to non-class type 'Int'

If we use a class,

weak var pizzatoo:Pizza = Pizza()

We get a different error
'weak' variable should have optional type 'Pizza?'

To use weak, have a class and an optional:

weak var pizzatoo:Pizza? = Pizza()

10. Use optional binding

In the control statements if and while the conditional statement can be replaced with an assignment of a constant. This is known as optional binding. For example:

if  let aButton = myButton{
     aButton.tintColor = UIColor.redColor()
     let type = aButton.buttonType
     let title = aButton.currentTitle
  ...
}

The constant aButton, if myButton is non-nil, executes the code with aButton used as an unwrapped myButton.  This is particularly useful if you has several things you needed unwrapped all at once. With some weak references, it is the only way you can get the values. Here’s an example from my recent post on Split View Controllers. The only way to access the outlets in the detail  view is through optional binding:

     func configureView() {
        // Update the user interface for the detail item.
        if let detail = self.detailItem as? Pizza {     //if we have an object in the detail item
            pizza = detail
            if let label = self.pizzaTypeLabel {
                label.text = detail.pizzaType
            }
            let pizzaSizeString = NSString(format:"%6.1fin Pizza",detail.pizzaDiameter)
            let priceString = NSString(format:"%6.2f sq in at $%6.2f is $%6.2f",detail.pizzaArea(),detail.unitPrice(),detail.pizzaPrice())
            if let label = self.pizzaSizeLabel{
                label.text = pizzaSizeString
            }
            if let label = self.pizzaPriceLabel{
                label.text = priceString
            }

        }

    }

In this example, you have to bind IBOutlets of UILabels and their controller to get anything in or out of them. They are too weak to exist without the optional binding.

In later versions of Swift Apple introduced guard, whihc works similar to if but has a bit more flexibilty in unwrapping options. The button code above, for example would look like this

guard let aButton = myButton else { return }
     aButton.tintColor = UIColor.redColor()
     let type = aButton.buttonType
     let title = aButton.currentTitle
  ...

The guard unwraps and assigns like any other constant, but has a handler for nil as the else clause block. Here I return if myButton is nil and do nothing. This works the same as if but prevents the nasty nesting we have in the configureView function above.

Optionals are powerful, and in time most Swift Developers will think that Objective-C pointers are limited cousins to the flexibility of optionals. As developers get used to Swift, these will become second nature. Until then, hopefully these ten points will help guide the confused in how to use optionals with the least amount of frustration.

The Swift Swift Tutorial: How to Use Split View on iPad (Round 1)

Back in July of 2014, I wrote this first post on using UISplitViewController in Swift. I gave how to use Apple’s template, which has a few interesting quirks, such as ARC killing@IBOutlets at the drop of a hat. I said I would write another post on the subject. I ended up making this section 2 of Chapter six of my Book Swift Swift: UI View Controllers . iOS8 makes split views universal, not just iPad. It does different things depending on class sizes. I thought I’d share with you a preview of that book.It came out as three projects, which if you want and updated version without ARC killing your detail’s outlets, go here to start the series: Swift Swift: Split View Controllers (Round Two) Part 1: The Master-Detail template For more previews and interesting thoughts, sign up for my newsletter. –Steve

Two and a half weeks ago I had a request from a reader to cover split views and split view controllers in Swift. My apologies for taking this long, but now I know the reason for this user’s request. For those not familiar with split view controllers, they are a view controller meant for the iPad. They take two view controllers and place them in the same window. On the left  is the master, and is almost always a table view controller. The other view controller is known as the detail, which is a standard UIView controller.

Portrait Split Image View
Portrait Split Image View

I will admit I had a lot of problems implementing split views in Swift. I finally did get it to work. There is a change in Swift’s implementation that requires a bit of getting used to. This will be the first round of split views. In this post, I’ll show how to use the master-detail template to get them to work. After I cover a few other topics which will be useful in a more advanced version, I’ll revisit split views in Swift.

The Problem with the Detail Controller

The way I learned to make a split view with Objective-C was by dragging out a split view controller on a blank storyboard, and writing everything myself. That doesn’t seem to work too well and I was getting one of Swift’s most common error messages: fatal error: Can't unwrap Optional.None This is the run-time error when you unwrap a nil value from an optional.  This has to do with very weak references. The error happens in @IBOutlets in the DetailViewController . Outlets are by definition weak, and by Swift’s definition anything weak is also an optional. According to Apple’s book Using Swift with Cocoa and Objective-C. but @IBOutlet var name:Type is all we need to write. The compiler assumes the rest for us and defines an outlet as @IBOutlet weak var name: Type! = nil

Outlets in the detail controller cannot be directly accessed like an outlet hooked up anywhere else on a storyboard. In Swift, outlets are so weak in this case they are usually nil. Somehow the meager connections to the split view controller are not enough to keep them alive, though with the equivalent code in Objective-C there are enough strong pointers. In Swift, if you try something simple like this in the detail:

pizzaLabel.text = "Veggie"

That run-time error fatal error: Can't unwrap Optional.None. appears.

Apple’s Master-Detail template does work however.  There is a way to do this. In this lesson, we’ll set up the template to make a split view for PizzaDemo. We’ll cut and paste a lot of code from our last post on table views, and add a few new parts for the Split controller. As an iPad application, we will change our UI a bit from the iPhone. We’ll make our master the list of pizza types, and replace the segmented control with the master. In this session, we will not change the prices, but just get the data displayed in the model.

Get the template

In Xcode start a new project on the welcome screen, press command-shift-N or go to File>New>Project… Select in the iOS Application the Master-Detail Application. Name the app SwiftSplitPizzaDemo select Swift for the language and iPad for the device. Keep Core Data unchecked.

Select your file location and click Create. In the files created click the story board and look at what Xcode loaded for you.

The split view controller splits into two navigation controllers. One navigation controller has a UITableViewController subclass found in MasterViewController.swift. The other navigation controller has a UIViewController  subclass, found in DetailViewController.swift.

Xcode provides some sample code in the template to run an small application. Change to the iPad2 simulator, since it will fit on any screen nicely. Build and run. You get a pretty blank screen with a label on it. On your keyboard press the command key and the left or right arrow keys together a few times to rotate the ipad. As you change the orientation, the screen changes. Go back to portrait, and swipe right. the master view slides out. Press the plus button in the corner of the master view to add to the table. Rotate to a landscape view and hit plus again. Now select one of your two selections in the table view. The label in the detail changes.

We know what is there works. Now to replace this code with our own. Go to the storyboard. Start by selecting the dynamic table cell, changing the table cell to tableCell in the storyboard.  Also change the cell style to right detail.

Add the Model

Open the PizzaDemo application with the table view in it from our last lesson, if you want to copy and paste from there. If you do not have it, you can download it from github or you can copy what I have below.

We first need a Model. if you have the PizzaDemo code open, drag the Pizza.swift file over to SwiftSplitPizzaDemo. make sure Copy items if needed has a check next to it, and hit Finish. If you do not have the PizzaDemo open and don’t want to load it, hit command-N on the keyboard, and make a Cocoa Touch class called Pizza in Swift which subclasses NSObject. In the new Pizza.swift file, add this code:

import Foundation
/* --------

Our model for MVC
keeps data  and calculations
about pizzas

------------*/

class Pizza {
    var pizzaPricePerInSq = ["Cheese": 0.03 , "Sausage": 0.06 , "Pepperoni": 0.05 , "Veggie": 0.04]
    var typeList:Array {  //computed property 7/7/14
    get{
        return Array(pizzaPricePerInSq.keys)
    }
    }

    let pi = 3.1415926

    var pizzaDiameter = 0.0
    let maxPizza = 24.0
    var pizzaType = "Cheese"

    var radius : Double {  //computed property
    get{   //must define a getter
        return pizzaDiameter/2.0
    }
    set(newRadius){ //optionally define a setter
        pizzaDiameter = newRadius * 2.0
    }
    }

    var area :  Double {
    get{
        return pizzaArea()
    }
    }

    func pizzaArea() -> Double{
        return radius * radius * pi
    }

    func unitPrice() ->Double{
        let unitPrice = pizzaPricePerInSq[pizzaType]    //optional type ?Double
        if unitPrice != nil {
            return unitPrice!
        }                               //optional type ?Double checking for nil
        else {
            return 0.0
        }
    }

    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
    }

    func diameterFromString(aString:NSString) -> Double {
        switch aString {
        case "Personal":
            return 8.0
        case "10\"":
            return 10.0
        case "12\"":
            return 12.0
        case "16\"","15\"":
            return 16.0
        case "18\"":
            return 18.0
        case "24\"":
            return 24.0
        default:
            return 0.0
        }
    }

}

Add the Master

Go to the MasterViewController.swift file. Go into the MasterViewController class and comment out the following line:

var objects = NSMutableArray()

This will produce a lot of error messages. We will need to change the code in each place there is an error message. To add our model, just under where you commented out, objects add:

var pizza = Pizza()

Add the TableView Code

Scroll down to where the table view data source is:

   //MARK:  - Table View

    override func numberOfSectionsInTableView(tableView: UITableView?) -> Int {
        return 1
    }

    override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
        return objects.count
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell

        let object = objects[indexPath.row] as NSDate
        cell.textLabel.text = object.description
        return cell
    }

Replace it with the following code.

//MARK: -Table View
    override func numberOfSectionsInTableView(tableView: UITableView?) -> Int {
        // Return the number of sections.
        return 1
    }

    override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
        // Return the number of rows in the section.
        return pizza.pizzaPricePerInSq.count
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell
        let row = indexPath!.row   //get the array index from the index path
        let cell = tableView.dequeueReusableCellWithIdentifier("tableCell", forIndexPath: indexPath) as UITableViewCell  //make the cell
        let myRowKey = pizza.typeList[row]  //the dictionary key
        cell.textLabel!.text = myRowKey
        let myRowData = pizza.pizzaPricePerInSq[myRowKey]  //the dictionary value
        cell.detailTextLabel!.text = String(format: "%6.3f",myRowData!)
        return cell
    }

    override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat {
        return 44.0
    }

We will not be editing the table. Comment out this method for insertion:

/*
    func insertNewObject(sender: AnyObject) {
        if objects == nil {
            objects = NSMutableArray()
        }
        objects.insertObject(NSDate.date(), atIndex: 0)
        let indexPath = NSIndexPath(forRow: 0, inSection: 0)
        self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
    }
*/

Also comment out this method for deleting rows:

/*
    override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
        if editingStyle == .Delete {
            objects.removeObjectAtIndex(indexPath.row)
            tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
        } else if editingStyle == .Insert {
            // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
        }
    }
*/

Disable editing of the table by returning false here:

    override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
        // Return false if you do not want the specified item to be editable.
        return false
    }

We just made a table. We covered how to do that last time. Here we begin to see something new. Change tableview(didSelectRowAtIndexpath:indexPath:) to this:

    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        //let object = objects[indexPath.row] as NSDate
        //self.detailViewController!.detailItem = object
        pizza.pizzaType = pizza.typeList[indexPath.row] //set to the selected pizza
        if (detailViewController != nil){
            self.detailViewController!.detailItem = pizza //send the model to the detailItem
        }

    }

Looking at the top of our code the master view controller makes an instance of the DetailViewController class. Here we set the detailItem property of the detail view controller, whose type is AnyObject!. This is key to everything, but we’ll discuss the property more in a few minutes. We set the pizzaType property to the selected pizza, and then send the model to the detail controller via this property.

To be consistent, handle an ios8 issue, and to get rid of the last error, change prepareForSegue() like this:

 override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if segue.identifier == "showDetail" {
            let indexPath = self.tableView.indexPathForSelectedRow()
            //    let object = objects[indexPath.row] as NSDate
            //((segue.destinationViewController as UINavigationController).topViewController as DetailViewController).detailItem = object
            pizza.pizzaType = pizza.typeList[indexPath!.row] //set to the selected pizza
            ((segue.destinationViewController as UINavigationController).topViewController as DetailViewController).detailItem = pizza
        }
    }

Line 7 will not make a lot of sense if you are unfamiliar with split view controllers. Take a look at the storyboard:

The storyboard
The storyboard

One of a split view controller’s properties is an array called viewControllers. At most it has two objects,  navigation controllers for the master and the detail views. Embedded in each of these navigation controllers is a master and detail view. For the segue, we go from the master controller to the navigation controller, which is the segue’s destination. The first view controller in the destination navigation view controller’s array of controllers is the detail view. We access that by ((segue.destinationViewController as UINavigationController).topViewController Once we have the detail view controller, we assign the model to the detailItem property.

Now change viewDidLoad to this:

override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
/* Removed code for editing and use of bar buttons.
        self.navigationItem.leftBarButtonItem = self.editButtonItem()

        let addButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "insertNewObject:")
        self.navigationItem.rightBarButtonItem = addButton
*/
        let controllers = self.splitViewController!.viewControllers
        self.detailViewController = controllers[controllers.endIndex-1].topViewController as? DetailViewController

        }

Lines 10 and 11 set up the detail view controller in the master. It takes the detail view from the viewControllers array we assigned to controllers We also removed the bar button item navigation bar stuff since we will not be using it in this tutorial.

We now have a completed master view controller. Essentially it is a table view with some odd connections to a property in the detail view controller. Build and run. In portrait swipe from left to right. Try rotating the iPad and see what happens.

Add the Detail

The next step is to design the detail view controller. We will be showing a lot more information on this scene, so a good layout is important.

Design and Connect the Scene

Go into the storyboard and go to the detail view controller there. Add five labels  with the text Pizza:, Size:, Anchovy, 12″, and Pizza Info And Price. Add one button with a blue background and white foreground labeled 10″. Copy the button five times. Resizing as necessary, lay out and label everything something like this:

Layout for the detail view

I did use auto layout here since we will be rotating the iPad.

Open the assistant editor to the detail view controller and add the following outlets by control dragging from the Anchovy, 12″, and Pizza Info And Price labels on the storyboard.

    @IBOutlet var pizzaSizeLabel: UILabel!
    @IBOutlet var pizzaTypeLabel: UILabel!
    @IBOutlet var pizzaPriceLabel: UILabel!

Adding Code for the DetailViewController

first, add the model to the view

var pizza = Pizza()

Next, add this code for the size buttons.

@IBAction func pizzaSizeButton(sender: UIButton) {
pizza.pizzaDiameter = pizza.diameterFromString(sender.titleLabel.text)
configureView()
}

Drag from the circle to each of the buttons to connect them all to this method.

Take a look at this code:

    var detailItem: AnyObject? {
        didSet {
            // Update the view.
            self.configureView()
//comment out this if clause  if you want the master to not disappear.
            if self.masterPopoverController != nil {
                self.masterPopoverController!.dismissPopoverAnimated(true)
            }
        }
    }

Lines 1-4 are at the heart of the way we get outlets to work. The MasterViewController instantiated a detailViewController. The master then set this property detailItem with the model, which is the data we wanted to pass to the detail controller. While just displaying is not strong enough a connection to keep the outlets alive, this connection is. To use an outlet we refer to detailItem. When set, detailItem calls configureView(), which looks like this:

    func configureView() {
        // Update the user interface for the detail item.
        if let detail: AnyObject = self.detailItem {
            if let label = self.detailDescriptionLabel {
                label.text = detail.description
            }
        }
    }
  

This is Apple’s template code to put text on a label. In line 2 we do two things at once. First we assign detailItem to a new constant detail. If that is successful, the app moves on to assigning another variable label to a UILabel. If that is successful, the app uses the assigned label to change the label properties. We sneakily assigned a strong pointer to a bunch of weak ones, at least for the life of the method. This is the way to access IBOutlets. Replace this code with the following:

     func configureView() {
        // Update the user interface for the detail item.
        if let detail = self.detailItem as? Pizza {     //if we have an object in the detail item
            pizza = detail
            if let label = self.pizzaTypeLabel {
                label.text = detail.pizzaType
            }
            let pizzaSizeString = NSString(format:"%6.1fin Pizza",detail.pizzaDiameter)
            let priceString = NSString(format:"%6.2f sq in at $%6.2f is $%6.2f",detail.pizzaArea(),detail.unitPrice(),detail.pizzaPrice())
            if let label = self.pizzaSizeLabel{
                label.text = pizzaSizeString
            }
            if let label = self.pizzaPriceLabel{
                label.text = priceString
            }

        }

    }

A few things changed  in configureView() for this example. In line 2, since there is a single detail view,  detailItem receives only the model.  I cast this directly to a instance of Pizza. I assign this to the model in the detail controller. While the outlets can be set here in configureView() everything else won’t be. If we want the properties from the buttons to work, we need to set the model here. After setting detail, we follow the if let label= pattern for updating our labels. That’s the trick to getting outlets to work in split views.

The Model initializes to the same values on both controllers, head over to the master view controller’s viewDidLoad() and add the following line:

pizza.pizzaPricePerInSq["Pepperoni"] = 9.99

This places a marker to prove our connections work.
Build and run. Play around with the app in different orientations.
splitview demo 4

There’s Always One More Bug

If you try to change the size of a pizza before selecting one in the master view, you’ll find nothing happens. We need to run configureView() in the detailViewController to get it to work. Go back to the MasterViewController‘s viewDidLoad() and add the following:

//send the model to the detailItem
        if (detailViewController){
            self.detailViewController!.detailItem = pizza

The pizza = detail assignment buried in line 4 of configureView() connects up enough to get the buttons to work.

This code works, and using the master-detail template is easy, though cumbersome, once you know the trick I showed here. There are still a lot of unanswered questions. We’ll explore some of those in the upcoming installments.

The Whole Code

Here is the code for the model, master view controller and detail view controller for copying and review. You can find a copy of the completed  project at Github as well.

Pizza.Swift

//
//  Pizza.swift
//  pizzaDemo
//
//  Created by Steven Lipton on 7/1/14.
//  Copyright (c) 2014 Steven Lipton. All rights reserved.
//

import Foundation
/* --------

Our model for MVC
keeps data  and calcualtions
about pizzas

------------*/

class Pizza {
    var pizzaPricePerInSq = ["Cheese": 0.03 , "Sausage": 0.06 , "Pepperoni": 0.05 , "Veggie": 0.04]
    var typeList:Array {  //computed property 7/7/14
    get{
        return Array(pizzaPricePerInSq.keys)
    }
    }

    let pi = 3.1415926

    var pizzaDiameter = 0.0
    let maxPizza = 24.0
    var pizzaType = "Cheese"

    var radius : Double {  //computed property
    get{   //must define a getter
        return pizzaDiameter/2.0
    }
    set(newRadius){ //optionally define a setter
        pizzaDiameter = newRadius * 2.0
    }
    }

    var area :  Double {
    get{
        return pizzaArea()
    }
    }

    func pizzaArea() -> Double{
        return radius * radius * pi
    }

    func unitPrice() ->Double{
        let unitPrice = pizzaPricePerInSq[pizzaType]    //optional type ?Double
        if (unitPrice != nil) {
            return unitPrice!
        }                               //optional type ?Double checking for nil
        else {
            return 0.0
        }
    }

    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
    }

    func diameterFromString(aString:NSString) -> Double {
        switch aString {
        case "Personal":
            return 8.0
        case "10\"":
            return 10.0
        case "12\"":
            return 12.0
        case "16\"","15\"":
            return 16.0
        case "18\"":
            return 18.0
        case "24\"":
            return 24.0
        default:
            return 0.0
        }
    }

}

MasterViewController.swift

//
//  MasterViewController.swift
//  SwiftSplitPizzaDemo
//
//  Created by Steven Lipton on 7/17/14.
//  Copyright (c) 2014 Steven Lipton. All rights reserved.
//

import UIKit

class MasterViewController: UITableViewController {

//MARK: Life Cycle
    var detailViewController: DetailViewController? = nil
    //var objects = NSMutableArray()
    var pizza = Pizza()

    override func awakeFromNib() {
        super.awakeFromNib()
        self.clearsSelectionOnViewWillAppear = false
        self.preferredContentSize = CGSize(width: 320.0, height: 600.0)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
/* Removed code for editing and use of bar buttons.
        self.navigationItem.leftBarButtonItem = self.editButtonItem()

        let addButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "insertNewObject:")
        self.navigationItem.rightBarButtonItem = addButton
*/
        let controllers = self.splitViewController!.viewControllers
        self.detailViewController = controllers[controllers.endIndex-1].topViewController as? DetailViewController

        pizza.pizzaPricePerInSq["Pepperoni"] = 9.99

        //send the model to the detailItem
        if (detailViewController != nil ){
            self.detailViewController!.detailItem = pizza
        }

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
/*
    func insertNewObject(sender: AnyObject) {
        if objects == nil {
            objects = NSMutableArray()
        }
        objects.insertObject(NSDate.date(), atIndex: 0)
        let indexPath = NSIndexPath(forRow: 0, inSection: 0)
        self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
    }
*/
//MARK: - Segues

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if segue.identifier == "showDetail" {
            let indexPath = self.tableView.indexPathForSelectedRow()
            //    let object = objects[indexPath.row] as NSDate
            //((segue.destinationViewController as UINavigationController).topViewController as DetailViewController).detailItem = object
            pizza.pizzaType = pizza.typeList[indexPath!.row] //set to the selected pizza
            ((segue.destinationViewController as UINavigationController).topViewController as DetailViewController).detailItem = pizza
        }
    }
//MARK:  - Table View Delegates
    override func numberOfSectionsInTableView(tableView: UITableView?) -> Int {
        // #warning Potentially incomplete method implementation.
        // Return the number of sections.
        return 1
    }

    override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete method implementation.
        // Return the number of rows in the section.
        return pizza.pizzaPricePerInSq.count
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell {
        //note I did not check for nil values. Something has to be really broken for these to be nil.
        let row = indexPath!.row   //get the array index from the index path
        let cell = tableView.dequeueReusableCellWithIdentifier("tableCell", forIndexPath: indexPath!) as UITableViewCell  //make the cell
        let myRowKey = pizza.typeList[row]  //the dictionary key
        cell.textLabel!.text = myRowKey
        let myRowData = pizza.pizzaPricePerInSq[myRowKey]  //the dictionary value
        cell.detailTextLabel!.text = String(format: "%6.3f",myRowData!)
        return cell
    }

    override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return 44.0
    }

    override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
        // Return false if you do not want the specified item to be editable.
        return false
    }
/*
    override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
        if editingStyle == .Delete {
            objects.removeObjectAtIndex(indexPath.row)
            tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
        } else if editingStyle == .Insert {
            // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
        }
    }
*/
    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        //let object = objects[indexPath.row] as NSDate
        //self.detailViewController!.detailItem = object
        pizza.pizzaType = pizza.typeList[indexPath.row] //set to the selected pizza
        if (detailViewController != nil){
            self.detailViewController!.detailItem = pizza //send the model to the detailItem
        }

    }

}

DetailViewController.Swift

//
//  DetailViewController.swift
//  SwiftSplitPizzaDemo
//
//  Created by Steven Lipton on 7/17/14.
//  Copyright (c) 2014 Steven Lipton. All rights reserved.
//

import UIKit

class DetailViewController: UIViewController, UISplitViewControllerDelegate {

    @IBOutlet var pizzaSizeLabel: UILabel!
    @IBOutlet var pizzaTypeLabel: UILabel!
    @IBOutlet var pizzaPriceLabel: UILabel!
    @IBOutlet var detailDescriptionLabel: UILabel!
    var pizza = Pizza()

    var masterPopoverController: UIPopoverController? = nil

    var detailItem: AnyObject? {
        didSet {
            // Update the view.
            self.configureView()

            //Comment out this if clause if you don't want the Master to disappear.
             if self.masterPopoverController != nil {
                self.masterPopoverController!.dismissPopoverAnimated(true)
            }
        }
    }

    @IBAction func pizzaSizeButton(sender: UIButton) {
         pizza.pizzaDiameter = pizza.diameterFromString(sender.titleLabel!.text!)
        configureView()
    }

    func configureView() {
        // Update the user interface for the detail item.
        if let detail = self.detailItem as? Pizza {     //if we have an object in the detail item
            pizza = detail
            if let label = self.pizzaTypeLabel {
                label.text = detail.pizzaType
            }
            let pizzaSizeString = NSString(format:"%6.1fin Pizza",detail.pizzaDiameter)
            let priceString = NSString(format:"%6.2f sq in at $%6.2f is $%6.2f",detail.pizzaArea(),detail.unitPrice(),detail.pizzaPrice()) //added 6/29/14
            if let label = self.pizzaSizeLabel{
                label.text = pizzaSizeString
            }
            if let label = self.pizzaPriceLabel{
                label.text = priceString //added 6/29/14
            }

        }

    }

    //MARK: - Life Cycle
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        self.configureView()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    //MARK: - Split View

    func splitViewController(splitController: UISplitViewController, willHideViewController viewController: UIViewController, withBarButtonItem barButtonItem: UIBarButtonItem, forPopoverController popoverController: UIPopoverController) {
        barButtonItem.title = "Master" // NSLocalizedString(@"Master", @"Master")
        self.navigationItem.setLeftBarButtonItem(barButtonItem, animated: true)
        self.masterPopoverController = popoverController
    }

    func splitViewController(splitController: UISplitViewController, willShowViewController viewController: UIViewController, invalidatingBarButtonItem barButtonItem: UIBarButtonItem) {
        // Called when the view is shown again in the split view, invalidating the button and popover controller.
        self.navigationItem.setLeftBarButtonItem(nil, animated: true)
        self.masterPopoverController = nil
    }

}