Accessing the Digital Crown in WatchOS

On the Apple Watch, the digital crown seems to be a great way to control your watch. the Slider and picker controls do use it, but direct developer use was prohibited in Watch OS2. In a nice change, Watch OS3 does. In this lesson, we’ll show how to get data from the digital crown in your applications.

Start a new Xcode WatchOS project DigitalCrownDemo. Deselect the notifications and save the project.

2016-08-22_06-26-21

Add three labels to the watch interface. Title them as follows:

2016-08-22_05-51-19

In the assistant editor, Connect the labels to outlets like this:

@IBOutlet var statusLabel: WKInterfaceLabel!
@IBOutlet var valueLabel: WKInterfaceLabel!
@IBOutlet var rpsLabel: WKInterfaceLabel!

We’ll track two data values the rotation speed and a value based on the change in value from the watch. Add them to the controller

var rps = 0.0
var value = 0.0

Add a update method for the display:

func updateLabels(){
    valueLabel.setText(String(format:"Value:%1.3f",value))
    rpsLabel.setText(String(format:"RPS:%1.3f rps",rps))
}

The crown uses a delegate. In interface controller, Adopt the <code>WKCrownDelegate</code>
class InterfaceController: WKInterfaceController,WKCrownDelegate {

The crown must have focus to return information. In awake(withContext) set the focus for the crown

    
override func awake(withContext context: AnyObject?) {
    super.awake(withContext: context)
    crownSequencer.focus()
    crownSequencer.delegate = self
}

The interface controller has a property crownSequencer that monitors the activity on the crown. We set the focus to crownSequencer and set the delegate method locations to this class.

Add the crownDidRotate delegate method, which returns values when there is a change in the rotation of the crown.

//MARK: Delegates
func crownDidRotate(_ crownSequencer: WKCrownSequencer?, rotationalDelta: Double) {
   value += rotationalDelta
   rps = (crownSequencer?.rotationsPerSecond)!
   statusLabel.setText("Moving")
   updateLabels()        
}

We have two arguments in this delegate method: the crownSequencer and the rotationalDelta. The Rotational debts is the change between the current and last position of the crown's rotation. One of the properties of the crownSequencer is rotations per second, giving a speed to the rotations. The code places the rotational delta and the rotations per send into a string for output to the watch. Most likely we'll want a position from the rotational delta, so we've added it to value.

Add the crownDidbecomeIdle delegate method

 
func crownDidBecomeIdle(_ crownSequencer: WKCrownSequencer?) {
    rps = (crownSequencer?.rotationsPerSecond)!
    statusLabel.setText("Stopped")
    updateLabels()
}

This delegate method fires when the crown stops. The code prints that the crown stopped and the last rotational speed before it did.

Run the code on the 42mm simulator. To use the crown hardware in the simulator, After a single click on the background of the watch face drag up and down two fingers. Stopping and starting the drag, you will see the display change.

The crownSequencer is smart enough to know your watches orientation from your settings. Up is always a positive value and down is always negative. there's a lot to do with this, from making new controls for your watch to an input for small games with sprite kit and scene kit.

The Whole Code

//
//  InterfaceController.swift
//  DigitalCrownDemo WatchKit Extension
//
//  Created by Steven Lipton on 8/22/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import WatchKit
import Foundation


class InterfaceController: WKInterfaceController, WKCrownDelegate {
    @IBOutlet var statusLabel: WKInterfaceLabel!
    @IBOutlet var valueLabel: WKInterfaceLabel!
    @IBOutlet var rpsLabel: WKInterfaceLabel!

    var value = 0.0
    var rps = 0.0
    
    func updateLabels(){
        valueLabel.setText(String(format:"Value:%1.3f",value))
        rpsLabel.setText(String(format:"RPS:%1.3f rps",rps))
    }
    
    override func awake(withContext context: Any?) {
        super.awake(withContext: context)
        crownSequencer.focus()
        crownSequencer.delegate = self
    }
    //MARK: Delegates
    func crownDidRotate(_ crownSequencer: WKCrownSequencer?, rotationalDelta: Double) {
        value += rotationalDelta
        rps = (crownSequencer?.rotationsPerSecond)!
        statusLabel.setText("Moving")
        updateLabels()
        
    }
    
    func crownDidBecomeIdle(_ crownSequencer: WKCrownSequencer?) {
        rps = (crownSequencer?.rotationsPerSecond)!
        statusLabel.setText("Stopped")
        updateLabels()

    }

    
}

How to Make Local Notifications in iOS 10

I’ve written before about local notifications in iOS. However, Apple has changed all that starting in iOS 10. In versions earlier than iOS 10, notifications were part of UIKit, but required the developer to make changes to the view controller and app delegate. Different types of notifications had different code. In iOS 10 notifications unifies the coding of notifications into the view controller.

There are two types of notifications: Local and remote notifications. Remote notification require connection to a server which pushes the notification to your device. These are beyond the scope of this lesson. Local notifications are notifications for events that happen on your device. We’ll set up these types of notifications in this lesson

Make A New Project

Make new single-view project named LocalNotificationDemo with Swift as the language. Go to the storyboard. Add a button centered on the view labeled 10 Second Notification. Using the align button alignment iconin auto layout, center the button like this.

2016-08-08_05-42-13

Be sure to check the update frame Items to New Constraints. You’ll have a storyboard like this:

2016-08-08_05-46-41

Open the assistant editor and make an action send10SecNotification to the button by control-dragging to the view controller code. Close the assistant editor.

Add the Framework

Notifications are a new framework. Go the to the viewcontroller.swift code and add the following under import UIKit :

import UserNotifications

This added the framework for the user notifications.

Check for Permission

Before any notification, we’ll need to check if the user allows us to use notifications. We only do this once, so add this to viewDidLoad:

override func viewDidLoad()
     super.viewDidLoad()
     UNUserNotificationCenter.current().requestAuthorization(
         options: [.alert,.sound,.badge],
         completionHandler: { (granted,error) in
             self.isGrantedNotificationAccess = granted
         }
    )
}

The object UNUserNotificationCenter.current() returns the current notification center. We ask for authorization with the requestAuthorization(options:completionHander) method. We have three options we can use to give an alert:

  • .alert – Display the alert window and text of the notification
  • .sound – Plays a sound or vibrates. On Apple Watch uses a haptic.
  • .badge – Places the red dot of the app to give a count of notifications from the app.

The argument options is an array. We add which of these three we wish to use for our notification.

The completion handler has two arguments grants and error. You can use these to handle the user preferences in notifications. Here I’ve set a Bool value I’ll use in the action. Add the property to the viewController class

var isGrantedNotificationAccess:Bool = false

Add this to the send10SecNotification action:

@IBAction func send10SecNotification(_ sender: UIButton) {
    if isGrantedNotificationAccess{
        //add notification code here
    }
}

This prevents the notification from scheduling if the user doesn’t want one.

Setting up Content

The notification is put together a lot like an onion – it has layers on top of layers. One layer creates the content of the notification . Add this under the //add notification code here comment:

let content = UNMutableNotificationContent()
content.title = "10 Second Notification Demo"
content.subtitle = "From MakeAppPie.com"
content.body = "Notification after 10 seconds - Your pizza is Ready!!"
content.categoryIdentifier = "message"

The UNMutableNotificationContent object contains all the content in a notification. We’ve included all the text properties title, subtitle and body, but you can add media content as well. An important property is the categoryIdentifier, which will help us in a few steps associate actions to the notification.

Setting Up Triggers

Triggers are the events which set off a notification. In earlier versions of iOS, there was only one easily accessible trigger: time expressed as a date. For local notifications, you have triggers for times, dates, and locations. There’s one more type of trigger for a remote notification. We’ll use the time interval trigger to set a trigger for ten seconds.

let trigger = UNTimeIntervalNotificationTrigger(
    timeInterval: 10.0,
    repeats: false)

For the time interval trigger, we have another new argument: repeats. If you need multiples of the same notification,set this to true. This is a feature added to iOS 10. In earlier versions of notifications, multiple notifications needed to be scheduled clogging up you limited number of notifications. If repeats is true, then there is only one. The notification triggers, deletes itself from the schedule then re-assigns itself.

Adding the Notification Request

We’ll take the trigger and content, add a string to name the request and whip them together in a UNNotificationRequest like this:


let request = UNNotificationRequest(
    identifier: "10.second.message",
    content: content,
    trigger: trigger
)

The identifier plays an important role. The system will prevent conflicts of the same notifications firing simultaneously. The watch and the phone may want to display the same local notification, but the system will see the identifier, and choose only one. The identifier also gives the developer control of the notifications. To stop a repeating UNTimeIntervalNotificationTrigger, there is a method to delete the notification you’d have to implement.

Adding the Notification

Once you have a request, add it to the current notification center

UNUserNotificationCenter.current().add(
    request, withCompletionHandler: nil)

Build and Run. On the first execution, we’ll be asked for permission to use notifications. After this first answer, the device will store the answer in the settings for the app.

2016-08-08_06-28-13

The user can change it at any time in the user settings, though the change will not take until the next start of the application.

Allow notifications. When the application appears, Press the button and then press Command-Shift-H to go to the home screen. Wait ten seconds. You will see this:

2016-08-08_06-29-00

Click on the notification, and you are back in the app. Click the button again, and press Command-L to simulate the user locking their phone. After ten seconds you get this:

2016-08-08_06-31-46

Click on the notification then open the phone with Command-Shift-H for the home button. You are back in the app.

That’s how to make a basic notification in iOS. You can use the same code in WatchOS to get local notifications as well. There’s a lot more to explore in notifications, but that will be in another lesson.

The Whole Code

//
//  ViewController.swift
//  LocalNotificationDemo
//  A Very Basic Local Notification
//  Created by Steven Lipton on 8/8/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import UIKit
import UserNotifications

class ViewController: UIViewController, UNUserNotificationCenterDelegate {
    var isGrantedNotificationAccess:Bool = false
    @IBAction func send10SecNotification(_ sender: UIButton) {
        if isGrantedNotificationAccess{
            //add notification code here
            
            //Set the content of the notification
            let content = UNMutableNotificationContent()
            content.title = "10 Second Notification Demo"
            content.subtitle = "From MakeAppPie.com"
            content.body = "Notification after 10 seconds - Your pizza is Ready!!"
            
            //Set the trigger of the notification -- here a timer. 
            let trigger = UNTimeIntervalNotificationTrigger(
                timeInterval: 10.0,
                repeats: false)
            
            //Set the request for the notification from the above
            let request = UNNotificationRequest(
                identifier: "10.second.message",
                content: content,
                trigger: trigger
            )
            
            //Add the notification to the currnet notification center
            UNUserNotificationCenter.current().add(
                request, withCompletionHandler: nil)
            
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        UNUserNotificationCenter.current().requestAuthorization(
            options: [.alert,.sound,.badge],
            completionHandler: { (granted,error) in
                self.isGrantedNotificationAccess = granted
            }
        )
    }



}

Using Observers and Delegates on the Model

Model view controller can sometimes seem difficult. You have something to do that needs the controller, but has no good way to get there.  One common example is refreshing a display. We change data , but how do we tell the controller when there is fresh data to update the view. Often, we add a method for display refresh everywhere we change the data in the view controller. This can get messy. Another way is using notifications with  key-value. There is a third in Swift called property observers which may provide an easier solution to those types of problems.

In this lesson, we’ll use property observers and delegates to refresh data in the view.

Set up the project

Start a new single view  Xcode Project called PropertyObserverDemo. Set Swift for the language and a Universal  device.   Go to the story board and add six labels a switch and a stepper. Arrange them like this:

2016-08-01_07-11-48

For the Ice Cream Label, using text styles set the font to Title 1.

2016-08-01_05-54-31

Set the font for Ice cream description to Headline. Set the Font for Price 000.00 to Title 2.

For the stepper set the attributes as follows:

2016-08-01_05-55-18

Change  the  ViewController  class to this:

class ViewController: UIViewController {
    @IBOutlet weak var iceCreamDescription: UILabel!
    @IBOutlet weak var iceCreamPriceLabel: UILabel!
    @IBAction func didChangeScoops(_ sender: UIStepper) {
    }
    @IBAction func didChooseCone(_ sender: UISwitch) {
    }
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

Connect IceCreamDescription to the Ice cream description Label by dragging from the circle to the label. Connect the  iceCreamPriceLabel to the Price 000.00 label the same way. Connect didChangeScoops to the stepper and didChooseCone to the switch.

Add a Model

Let’s add simple model to this. Press Command-N and add the following model named IceCream subclassing NSObject to the project:

class IceCream: NSObject {
    let pricePerScoop = 2.5
    var scoops =  0
    var isInCone = true
    var price:Double {
        get{
            let iceCreamPrice = Double(scoops) * pricePerScoop
            if isInCone {
                return iceCreamPrice + 0.25
            } else{
                return iceCreamPrice + 0.10
            }
        }
    }
}

This has three properties and a constant.  scoops and isInCone are properties, and our third property price is a computed property based on the values of scoops and isInCone.

A display method

Go back to the ViewController class. Above the outlets, add the model:

let iceCream = IceCream()

In viewDidLoad, add the following to initialize the model

override func viewDidLoad() {
    super.viewDidLoad()
    iceCream.isInCone = true
    iceCream.scoops = 1
}

Change our actions like this:

@IBAction func didChangeScoops(_ sender: UIStepper) {
    iceCream.scoops = Int(sender.value)
}
    
@IBAction func didChooseCone(_ sender: UISwitch) {
    iceCream.isInCone = sender.isOn
}

Add the following method:

func refreshDisplay(){
    var cone = "in a cone"
    if !iceCream.isInCone{
       cone = "in a dish"
    }
    iceCreamDescription.text = "\(iceCream.scoops) scoops " + cone
    iceCreamPriceLabel.text = String(
        format:"Price: %2.2f",
        iceCream.price)
}

This code is modifies the model by the stepper and switch. The refreshDisplay method changes the two labels to reflect these changes in the model. However nothing has called refreshDisplay yet.

A very common and simple way to call refreshDisplay is to add it to the actions and viewDidLoad. We could do this for example to didChangeScoops:

@IBAction func didChangeScoops(_ sender: UIStepper) {
    iceCream.scoops = Int(sender.value)
    refreshDisplay()
}

But that would require every method that changes a property of iceCream to call this method. That can be cumbersome. Another way is to have the model tell the controller there’s been a change.

Using Property Observers

In order to tell the controller, there’s two parts: knowing there is a change worth reporting and telling the controller. For knowing there’s a change we can use property observers. Property observers are additions to the property that run code when the property’s value changes. Two keywords didSet and willSet perform this. didSet executes the code in the block after the values changes and willSet before the value change. For example, you might have a property in some function like this to change from our programming index values starting with 0 to user-friendly values starting with 1:

var index = 1{
    didSet{
     index += 1
    }
}

Any time index changes, we add 1 to index. Property observes only fire after initialization. They do not fire for initializing the variable.

The model needs to tell the controller there’s been a change. This is another good use for a delegate. We can create a delegate method iceCreamDidChange with no arguments that runs in ViewController when there is a change to the properties. Almost always keep to using no arguments when using this. The only thing it does is states there is a change. Never pass values of the model. Use the model in your view controller to access the values. The only exception is an argument that tells the  view controller what changed in the model.

Add a Delegate

Go to the IceCream class. Add a protocol above it

protocol IceCreamDelegate{
    func iceCreamDidChange()
}

In IceCream, add a property delegate:

    var delegate:IceCreamDelegate? = nil

Notice I used IceCreamDelegate? not IceCreamDelegate! for the type. Usually I use IceCreamDelegate! in delegates between view controllers to force myself to set the delegate property. With IceCreamDelegate!, a nil causes a run time error. With this type of delegate, there may be times I want to shut down updating. By using IceCreamDelegate? and setting the delegate to nil in the view controller, I shut down the updating.

Add The Property Observers

Change scoops to this:

var scoops =  0{
    didSet{
        delegate?.iceCreamDidChange()
    }
}

The property observer calls the delegate method when there is a change in the property.  Now do the same to isInCone

var isInCone = true{
    didSet{
        delegate?.iceCreamDidChange()
    }
}

Depending on your application, you can set up property observers for the properties that notify the view controller and leave it off for properties that do not.

Adopt the Delegate

Go to ViewController. Adopt the delegate:

class ViewController: UIViewController,IceCreamDelegate {

Add the required method.

//MARK: Delegates
    func iceCreamDidChange() {
        refreshDisplay()
    }

This method only notifies the controller of the change. It’s up to the controller to do something about it by code in the method. In our case, we’ll refresh the display.

Finally, add the following line to viewDidLoad to locate the delegate

override func viewDidLoad() {
    super.viewDidLoad()
    iceCream.delegate = self
    iceCream.isInCone = true
    iceCream.scoops = 1
}

Set your simulator for an iPhone 6s. Build and Run

2016-08-01_07-41-38

Change the values and you’ll find the display updates correctly.

2016-08-01_07-42-44

2016-08-01_07-43-39

This is a quick method for controlling updating of models when the controller needs to. It has the advantage that any controller using the model can decide how it wants to handle the change. It has the disadvantage of hiding refreshing display code in the view controller and requiring several calls to the delegate in the model. You’ll find similar patterns to this in Apple’s API’s that provide delegates to handle events. For example UITableViewDelegate  has the tableView(_:didSelectRowAt:) delegate method for informing the view controller the user selected a row from the table view.

The Property Observer – Delegate pattern provides one way to inform your controller of events. There are other such as key:value observers, but I find for many applications this is enough to do the job.

The Whole Code

ViewController.swift

//
//  ViewController.swift
//  PropertyObserverDemo
//
//  Created by Steven Lipton on 7/31/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import UIKit

class ViewController: UIViewController,IceCreamDelegate {
    let iceCream = IceCream()
    @IBOutlet weak var iceCreamDescription: UILabel!
    
    @IBOutlet weak var iceCreamPriceLabel: UILabel!
    @IBAction func didChangeScoops(_ sender: UIStepper) {
        iceCream.scoops = Int(sender.value)
    }
    
    @IBAction func didChooseCone(_ sender: UISwitch) {
        iceCream.isInCone = sender.isOn
    }
    
    func refreshDisplay(){
        var cone = "in a cone"
        if !iceCream.isInCone{
            cone = "in a dish"
        }
        iceCreamDescription.text = "\(iceCream.scoops) scoops " + cone
        iceCreamPriceLabel.text = String(format:"Price: %2.2f",iceCream.price)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        iceCream.delegate = self
        iceCream.isInCone = true
        iceCream.scoops = 1
    }

   //MARK: Delegates
    func iceCreamDidChange() {
        refreshDisplay()
    }
}

IceCream.swift

//
//  IceCream.swift
//  PropertyObserverDemo
//
//  Created by Steven Lipton on 7/31/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import UIKit

protocol IceCreamDelegate{
    func iceCreamDidChange()
}

class IceCream: NSObject {
    let pricePerScoop = 2.5
    var delegate:IceCreamDelegate? = nil
    var scoops =  0{
        didSet{
            delegate?.iceCreamDidChange()
        }
    }
    var isInCone = true{
        didSet{
            delegate?.iceCreamDidChange()
        }
    }
    var price:Double {
        get{
            let iceCreamPrice = Double(scoops) * pricePerScoop
            if isInCone {
                return iceCreamPrice + 0.25
            } else{
                return iceCreamPrice + 0.10
            }
        }
    }
}

What is Model-View-Controller?

If you are a beginning developer you’ll hear the term Model View Controller often, but may not understand what it is. You’ll see the UIViewController class in an app, but my not completely understand what is happening here. In this lesson, I’ll describe this system and develop an app to demonstrate MVC from the Xcode 8/Swift 3.0 edition of my book Swift Swift View Controllers coming in October, though available now for Xcode 7/Swift 2.1.

What is a Class?

A class is a collection of data, which we call properties, and actions we can do to those properties, which we call methods.
Properties and methods are either public or private. A public method is one that a class other than the defining class can see and can use. Private means that the property or method is only usable and visible within the defining class. Other classes cannot see or use it. In Swift the private keyword makes properties and methods private. Swift’s calculated properties feature is another way to make properties private. In Swift, the default state makes a method or class public to the current target, but not other targets.
We tend not to like other people messing with our insides, and that is a good programming practice too. It is best to leave what is public to a necessary minimum. Keeping properties and methods private and not exposing all of our class is known as encapsulation.
Encapsulation allows us to make code as modular as plastic building blocks. Just as a few stubs come out of an otherwise standard sized brick, only a few usable methods come out of a class. Then they can attach to a lot of other bricks.

What is MVC?

MVC schematic blankA term heard often when working with Xcode, is MVC. MVC stands for Model View Controller. It is not an exclusive iOS or OS X term. It is a pattern of programming, a good organization of any program or application in a graphics-rich environment, and arguably any environment that interacts with the user. MVC separates two major parts of an application, the data and the user interaction, then adds an intermediary between them. Why is this important? You might write and publish an application for an iPhone, then decide an iPad version would be a good idea, then decide to make a OS X or web version. With MVC, you only change one part completely, the view and possibly some of the controller. The code handling your data never changes between the versions saving a lot of time and effort.

What Are Models?

MVC schematic modelThere are parts of our program that deal with information we want to process. A pizza ordering system has a list of data giving us information about each person’s order. There may be other links to that data with more data about each customer, and about each pizza. In a pizza ordering system this is our model: the collection of all the data we will use in our ordering system. It does not in any way interact with the user. It does not display anything nor does it ask for input. It is just data. Here is a simple model:

class Pizza{
    var pizzaDiameter:Double
    var pizzaTopping:String
    init(topping:String, diameter:String){
        pizzaDiameter = diameter
        pizzaTopping = topping
    }
}

This model is two variables, pizzaDiameter and pizzaTopping. I can describe a 10 inch pepperoni pizza this way by Pizza(topping:"Pepperoni", diameter:10.0). This is by code only – there is no user input. More often, the model contains methods to manipulate the data we have. For example, the above model could store crust information for the pizza and compute the radius and area from the diameter. We’ll initialize the method by giving the topping, crust, and diameter.

class Pizza{
    var pizzaTopping:String
    var pizzaCrust:String
    var pizzaDiameter:Double
    var pizzaRadius:Double {
      get{
          return pizzaDiameter / 2.0 }
      }
    let pi = 3.1415926
    let crust = 0.1
    init(
        topping:String,
        crust:String,size:Double)
    {
        pizzaTopping = topping
        pizzaCrust = crust
        pizzaDiameter = size
     }
    func howMuchCheese(height:Double) -> Double{
        //Volume of pizza minus crust
        let c = pizzaRadius * pi * (1.0 - crust )
        return c * height
    }
}

This model is data for a basic pizza ordering system. There is still no user input or output here. Models might make calculations as we do in the howMuchCheese(height:) method, but again there is no user interaction here.

What are Views?

MVC schematic viewAll the user interaction happens is in the view. In Xcode, most people use Interface Builder to build their views. A developer can also programmatically create a view class to hold the different controls.
As the model never interacts with the user, the view never interacts directly with the model. The view doesn’t do much but sit there. It might respond to a user touch with feedback such as a color change when a but2016-07-19_06-56-52ton gets tapped or a scrolling motion at times, but that is all it does. The view does contain a lot of properties and methods and that tell us the state of the view. We can change the appearance and
behavior of the view through methods and properties. The view can tell the controller that there was a change in the view, such as a button getting pressed, or a character typed. The view can’t do anything about it, but it can broadcast something that the controller observes and reacts to.

What are Controllers?

MVC schematicThe heart of MVC connects these two. Called the controller or view controller, it coordinates what happens in the model and what happens in the view. If a user presses a button on the view, the controller responds to that event. If that response means sending messages to the model, the view controller does that. If the response requires getting information from the model, the controller does that too. In Xcode, @IBOutlet and @IBAction connect interface builder files with views to the view controller. For example, part of the view controller for the pizza app might be this:

 class ViewController:UIViewController {
//Instance of the model    
     let pizza = Pizza(
        topping: "Cheese",
        crust: "Pan",
        size: 10.0)
// a connection to change the view
    @IBOutlet weak var pizzaLabel: UILabel!
    //A connection to react to user action
    @IBAction func toppingSelectionButton(
        _ sender: UIButton) 
     {
        pizza.pizzaTopping = 
            (sender.titleLabel?.text)!
        displayPizza()
    }
//A function to change the label
    func displayPizza(){
        pizzaLabel.text = "\(pizza.pizzaDiameter)\""
             + pizza.pizzaTopping
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        displayPizza()
    }

Methods like displayPizza() access the model’s property pizzaTopping and pizzaDiameter, then send them on to the view. Methods like toppingSelectionButton react to user response on the view, sending information to the model.

Keeping Data Private

The key to MVC is communication, or rather the lack of it. MVC takes encapsulation very seriously. The view and the model never directly talk to each other. The controller can send messages to the view and the model. The controller never directly changes anything in either the view or the model.
As an aside, we need to talk about how Swift coddles us. For true encapsulation, our properties should all be private and we should write two special methods: a getter to retrieve a value and a setter to change a value for every property. For example the property pizzaDiameter might be written this way:

var pizzaDiameter:Double = 0.0
func setPizzaDiameter(size:Double){
    pizzaDiameter = size
}
func pizzaDiameter()->Double{
    return pizzaDiameter
}

Swift generates the setters and getters and type behind the scenes for you. You only need this

var pizzaSize = 0.0
var pizzaDiameter = 0.0

Then use it as a variable:

let z = pizzaDiameter / 2.0 //getter for pizzacrust
let a = 2.0 //height of crust
let v = pi * z * z * a //volume
pizzaSize = v //setter

This creates the illusion you are directly modifying a property. You don’t need the setter or getter. You will get an error if you try to write the code with setters and getters.
Why I mention this is the illusion of directly modifying properties in the view and model by the controller. It isn’t directly modifying anything. Behind the curtain are getter and setter methods. The model can not assign values in the controller. Then how do we assign values from the model to the controller? The model can return a value in  a getter called by the controller.
So to summarize, a view, a model and a controller cannot directly change a property in each other. A view and a model may not talk to each other at all. A view can tell the controller there is a change in the model. A controller can send messages in the form of method calls to the view and the model and get the responses back through that method. Now let’s look at a functional example in the next chapter.

Set Up the View

In Xcode, Create a new project as a Single-View Application with Swift as the language and a Universal Device. Call it SwiftMVCPizzaDemo.

2016-07-19_08-21-53

On the bottom left of the storyboard, you will find this:

2016-07-19_08-27-27

Make sure the button reads View as:iPhone 6s. If it does not , click where it says View as. You will get a toolbar of device frames appear underneath. Hovering over them, you will see a cursor indicating the device. Select the iPhone 6s, then click View as to dismiss the toolbar.

2016-07-20_05-34-04

In the storyboard add a label, two buttons, and a segmented control. Make one of the buttons with a Red(#FF0000) background and White(#FFFFFF) lettering, make the title of the button read Clear.
Stretch the segment across the device so both blue margins highlight. Make the segmented control four segments with Cheese, Sausage, Pepperoni,and Veggie.
Stretch the label across the device between the margins like the segmented control. Set the alignment to center  by clicking the center alignment button  button in the attributes inspector. Your layout should look something like this:

2016-07-21_08-44-05

Connect the View Controller

Open the assistant editor, and the ViewController.swift file should appear. Control drag the label to the code and release. Make an outlet displayLabel.

Control-drag from the unlabeled button to the code. Make a action named sizePizza. Remember to change from an outlet to an action since Interface Builder picks an outlet by default. Set the Type to UIButton.

Now do the same for the red button, making an action clearDisplay. Control drag from the segment to the code and make another action toppingSegment you now have the following outlets and actions

@IBOutlet weak var displayLabel: UILabel!
@IBAction func sizePizza(_ sender: UIButton) {
}
@IBAction func toppingSegment(_ sender: UISegmentedControl) {
}
    @IBAction func clearDisplay(_ sender: UIButton) {
}

 

A Multiple Selection Button

We will use preset buttons based on the size button to choose the size of a pizza. Go back into interface builder and click once on the blue button. Copy the button, then paste it four times for five buttons. Change the titles and arrange the buttons to look like this:

2016-07-21_08-43-38

This is a way to have multiple buttons use the same IBAction. The properties can be different for the buttons, in our case the titles. Each button reacts exactly the same to being pressed since they have the same IBAction method. Our model will take the title string and convert it into a pizza size.

Make the Basic Model and Properties

Open a new file by pressing Command-N. Make a new Cocoa Touch Subclass of NSObject called Pizza with Swift as the language. Add this code to the file that appears

class Pizza:NSObject {
    let pi = 3.1415926
    var diameter = 0.0
    var topping = "Cheese"
    var unitPrice = [
        "Cheese": 0.03 , 
        "Sausage": 0.06 ,
        "Pepperoni": 0.05 ,
        "Veggie": 0.04
   ]
}

With that small amount of code, we created a class with three properties and two constants. While there is a built-in constant M_PI, we’ll use our own value for pi in area and volume calculations. The class sets a default value for the pizza topping to Cheese. The unitPrice is a dictionary with pricing information.

Create Computed Properties

Besides declared properties like above, we’ll use some computed properties. Add the following code just under the pizzaPricePerInSq dictionary declaration.

//MARK: Computed Properties
var radius : Double { //1 -- computed property
//must define a getter for a computed property
    get{
        return diameter/2.0
    }
}

This declares the property radius based on the values of other properties. Computed properties must declare their type, and must include a getter.  In the radius property, we compute the radius we need for area calculations by dividing the already declared property diameter by 2.0, then return the value.
Add another computed property:

var area : Double{
        get{
            return pi * radius * radius
        }
    }

The area property returns a double with the area of our pizza using radius and pi. Add one more computed property to compute price for a whole pizza:

var price:Double{
        get{
            guard let price = unitPrice[topping]
            else{
                return 0.0
            }
            return price * area
        }
    }

Dictionary values are optional values, which means they could be nil or a value. We use the guard to unwrap the price of a pizza. if that pizza topping does not exist, the code returns 0. Otherwise the code returns the area multiplied by the unit price. I’m aware this is a very contrived way of figuring out a price of a pizza, but it works for our purposes.

Conversion Methods in Models

The view has a bunch of buttons with titles. The view controller will ask the view for the strings of those titles. Sizes in our model are type Double, not String. Models may have conversion methods to take one type and convert it to the type it needs. Add this method under price

//MARK: - Instance Methods
    func diameter(from string:String)-> Double{
        switch string {
        case "Personal":
            return 8.0
        case "Small":
            return 10.0
        case "Medium":
            return 16.0
        case "Large":
            return 18.0
        case "Jumbo":
            return 24.0
        default:
            return 0.0
        }
    }
}

The code uses a switch statement to parse the strings to numbers, returning the value as a Double .

Use the Model in the Controller

Open the ViewController.swift file. Controllers supervise both the model and the view. This app has a model and a view, but no real controller yet. View controllers need an instance of the model. Just under the view controller’s class declaration add the following:

let pizza = Pizza() //instantiate a Pizza object

This is all we need to instantiate a Pizza object: declare the object as a constant and give a class for the object. Even though pizza is a constant, its properties are not forced to be constants. Using let is better memory allocation than using var.
In this app, pressing the clearDisplay button will send a message from the view to the controller to run code to reset the application. That reset is a change of the view. Change the action clearDisplay to this:

@IBAction func clearDisplayButton(sender : UIButton) {
    displayLabel.text = iLovePizza
}

There’s a constant iLovePizza in this action. At the top of the class add a constant

let iLovePizza = "I Love Pizza!"

Another common MVC interaction is to take information from the model and present it on the view. In our app, we’ll do all that in the label. Add this display method for the label:

//MARK: - Instance Methods
func displayPizza(){
    let displayString = String(
    format:"%6.1f inch %@ $ %6.2f",
         pizza.diameter,
         pizza.topping,
         pizza.price
    )//2
    displayLabel.text = displayString
}

The method makes a string displayString from properties of pizza, then sends it to the label.
Often the user changes the model by an interaction with a view. Add the following code for the sizePizza button:

@IBAction func sizePizza(_ sender: UIButton) {
    let title = (sender.titleLabel?.text)!
    pizza.diameter = pizza.diameter(from: title)
    displayPizza()
}

The size button reads the title of the button called sender, and assigns the correct diameter for a pizza to pizza., using the conversion method diameter(from:)
In another example of changing the model by an interaction of the view, change the topping property through the segmented control. Add this to the code for the topping action

@IBAction func toppingSegment(_ sender: UISegmentedControl) {
    let index = sender.selectedSegmentIndex
    let title = sender.titleForSegment(at: index)!
    pizza.topping = title
    displayPizza()
}

The view will react to a change in segment by sending a change message to the controller. When this happens, the controller gets the title for the segment from the view then sets the model’s topping property to that title.
View controllers will need to do initialization to the model and view. often we do this in the viewDidLoad method. Find the viewDidLoad method in your ViewController class. Add the following code to it:

override func viewDidLoad() {
        super.viewDidLoad()
        displayLabel.text = iLovePizza
    }

This sets up the view with an initial value.
Set the simulator to an iPhone 6s. Build and run the application.

2016-07-25_06-49-35

Select a pizza, and you get  a price.

2016-07-25_06-49-59

The Whole Code

ViewController.swift

//
//  ViewController.swift
//  SwiftMVCPizzaDemo
//
//  Created by Steven Lipton on 7/19/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import UIKit


class ViewController: UIViewController {
    // Mark: Properties and Outlets
    let pizza = Pizza()
    let iLovePizza = "I Love Pizza!"
    @IBOutlet weak var displayLabel: UILabel!
    @IBAction func sizePizza(_ sender: UIButton) {
        let title = (sender.titleLabel?.text)!
        pizza.diameter = pizza.diameter(from: title)
        displayPizza()
    }
    @IBAction func toppingSegment(_ sender: UISegmentedControl) {
        let index = sender.selectedSegmentIndex
        let title = sender.titleForSegment(at: index)!
        pizza.topping = title
        displayPizza()
    }
    @IBAction func clearDisplay(_ sender: UIButton) {
        displayLabel.text = iLovePizza
    }
    
    //MARK: - Instance Methods
    func displayPizza(){
        let displayString = String(
            format:"%6.1f inch %@ $ %6.2f",
            pizza.diameter,
            pizza.topping,
            pizza.price
        )//2
        displayLabel.text = displayString
    }
        override func viewDidLoad() {
        super.viewDidLoad()
        displayLabel.text = iLovePizza
    }

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

Pizza.Swift

//
//  Pizza.swift
//  SwiftMVCPizzaDemo
//
//  Created by Steven Lipton on 7/20/16.
//  Revision for Swift 3.0
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import UIKit

class Pizza:NSObject {
    let pi = 3.1415926
    var diameter = 0.0
    var topping = "Cheese"
    var unitPrice = [
        "Cheese": 0.03 ,
        "Sausage": 0.06 ,
        "Pepperoni": 0.05 ,
        "Veggie": 0.04
    ]
    //MARK: -  Computed Properties
    var radius : Double { //-- computed property
        get{
            return diameter/2.0
        }
    }
    var area : Double{
        get{
            return pi * radius * radius
        }
    }
    var price:Double{
        get{
            guard let price = unitPrice[topping] else{
                return 0.0
            }
            return price * area
        }
    }
    //MARK: - Instance Methods
    func diameter(from string:String)-> Double{
        switch string {
        case "Personal":
            return 8.0
        case "Small":
            return 10.0
        case "Medium":
            return 16.0
        case "Large":
            return 18.0
        case "Jumbo":
            return 24.0
        default:
            return 0.0
        }
    }
    
    
}



How to Make a Tab Bar Controller in Swift 3.0 Code

While Navigation controllers often have the limelight when it comes to Xcode’s view  controllers, tab bar controllers are better for independent tasks in the same app, or for different ways of working with the same model. In this lesson we’ll take a look at  how to make them in Swift programmatically. If you are interested in tab bar controllers on the storyboard, You might want to read this post. For passing data between tabs read here.

Setting Up

While there are very easy storyboard ways of making tab bar controllers, we can do much of this programmatically. Start with a single view template and create a Swift project called TabProgDemo.  Once the project loads, on the keyboard hit Command-N. Create a  Cocoa Touch Class named PizzaViewController, subclassing of UIViewController.Make the language Swift, and check the mark to make a Xib file for an iPhone like this:

2016-07-12_10-38-48

Press Command-N again and Make another Cocoa Touch Class like the first, subclassing UIViewController. Name it PieViewController again do not creating a XIB file.

2016-07-13_06-44-57

I have created a file of image assets for this lesson. You can get them here as a zip file  or you can right click and save  the images below:

Pie Iconpizza icon

pizza_bar_icon@2xpizza_bar_iconpie_bar_icon@2xpie_bar_icon

Save these images on your drive. Open up your Assets.xcassets folder in Xcode. Select all the files in finder and drag them into the assets folder.

2016-07-13_06-15-30

Go to the the PieViewController.xib file. Make sure in the lower left of Interface builder it reads  the default setting:

view as 6s

 If, not set it to iPhone 6s by clicking it and selecting the device.  Set the background color to Light Green(#AAFFAA). Drag out a label and title it Pie at 32 points. Add the color Pie Icon. Your XIB should look like this:

2016-07-13_06-46-52

Select the PizzaViewController.xib file. Set up the Xib the same way as the pie controller. Use a Light Red(#FFAAAA) for the background:

2016-07-13_06-30-30

Tab Bar Programmatically

Click open the AppDelegate.swift file. While much of this can be important stuff, we’ll clear it out so we can concentrate on tab bar controllers. Replace the class with the following:

class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    func application(application: UIApplication,
                      didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        return true
    }
}

Let’s put this code together part by part. First add this under the function declaration:

let tabBarController = UITabBarController()

Creates an instance of a UITabBarController. This will be the controller we’ll configure in this function. We are doing this here so it is global to the application, one of the few times you want to do something globally.

Create the two view controllers from the XIB files. In full accordance with the adage “Life is uncertain, eat dessert first”, we’ll make the first page on launch be the pie page.

let tabViewController1 = PieViewController(
    nibName: "PieViewController",
    bundle: nil)
let tabViewController2 = PizzaViewController(
    nibName:"PizzaViewController",
    bundle: nil)

Add these three lines:

let controllers = [tabViewController1,tabViewController2]
tabBarController.viewControllers = controllers
window?.rootViewController = tabBarController

UITabBarController has a property called viewControllers, which is an array of the view controllers in the order displayed in the tab bar. Our first line of this segment makes an array of view controllers in the order we want them to present. Next we assign the array to the tab bar controller. The last line assigns the controller as the root view controller.

We have our controller set up, but no titles or icons. This code assigns the title and image.

tabViewController1.tabBarItem = UITabBarItem(
    title: "Pie",
    image: UIImage(named: "pie_bar_icon"),
    tag: 1)
 tabViewController2.tabBarItem = UITabBarItem(
    title: "Pizza",
    image:UIImage(named: "pizza_bar_icon") ,
    tag:2)

Each view controller has a property tabBarItem which contains the icon and text for the tab bar. We use the UITabBarItem(title:String,image:UIImage,tag:Int) initializer to create the tab bar items. In our example,  we set the first one to Pie and use the pie_bar_icon image  for the first. The second uses Pizza and the pizza_bar_icon  image.

Set your simulator to the iPhone 6s. Build and run. You get the Pie first:

 2016-07-14_05-35-42

You’ll notice the tabs the bottom. Tap the pizza tab.

2016-07-14_05-36-10

 

You have working tabs.

Programmatic Navigation View Controllers in Swift 3.0

Navigation controllers are the workhorse of  organizing view controllers. I’ve covered much of their use in other posts about MVC, segues and delegates. In this chapter, we’ll go through some of the Swift code for the Navigation controller.

The View Controller Stack

Navigation view controllers are stack based. The newest view controller is the visible one. It is on top of the last one we saw.
If you are not familiar with the stack data structure, it is useful to understand stacks and their nomenclature a little better. Stacks work a lot like a deck of cards that are face up.

2016-07-06_06-20-22

You can only see the top card. When you place a card on top of the stack, you push a card on the stack. When you remove a card from the stack and show the card below it, you pop it off the stack. We use the terms push and pop a lot to talk about stacks. The two major methods we’ll be talking about popViewController and pushViewController use this nomenclature to explain what they do.

Opening a View Controller in a Xib

Let’s look at a few ways to programmatically move through view controllers by pushing and popping to the navigation stack directly.
Start a new single view project in Swift called SwiftProgNavControllerDemo. Go into the storyboard. Make sure you have a iPhone 6s selected for View as: in the lower left of the storyboard”

2016-07-05_06-18-15

Select the blank view controller. Be sure to select the controller by clicking the view controller icon  icon and not the view. From the drop down menu select Edit>Embed in > Navigation Controller.
In the view controller, add a label and a button so your code looks like the diagram below. If you wish you can also color the background:

2016-07-06_07-49-58

Open the assistant editor. Control-drag the button and make an action for a UIButton called nextButton. Remove everything else from the class for now. Add the following two lines to the nextButton

let vc = TwoViewController(
     nibName: "TwoViewController",
     bundle: nil)
navigationController?.pushViewController(vc,
     animated: true)

Line 1 creates a view controller of class TwoViewController, using the XIB of the same name. Line 2 pushed the view controller on the navigation controller stack maintained by ViewController. Your code should look like this when done:

class ViewController: UIViewController {
    @IBAction func nextButton(_ sender: UIButton){
    let vc = TwoViewController(
        nibName: "TwoViewController",
        bundle: nil)
     navigationController?.pushViewController(vc,
        animated: true)
    }
}

We need another view controller as a destination. Press Command-N or click File>New>File… Choose a iOS source template of Cocoa Touch Class. Make the new file a subclass of UIViewController and name the file TwoViewController. We’ll work with a xib for our destination controller. Check on the option Also create XIB file.

2016-07-06_07-45-10

You will find a new xib in interface builder. Set it up to look like the illustration below.

2016-07-08_05-44-23

In the assistant editor, remove everything to have an empty class. Control-drag the Next button inside the TwoViewController class. Make an @IBAction method named nextButton  as an UIButton.  Add the following code to the nextButton() method:

let vc = ThreeViewController(
            nibName: "ThreeViewController",
            bundle: nil)
        navigationController?.pushViewController(vc,
                animated: true )

Your code should look like this:

class TwoViewController: UIViewController {
    
    @IBAction func nextButton(_ sender: UIButton) {
        let vc = ThreeViewController(
            nibName: "ThreeViewController",
            bundle: nil)
        navigationController?.pushViewController(vc,
            animated: true )
    }
}

Let’s do this one more time so we end up with three view controllers to push onto the view controller stack. Follow the same procedure as you did for TwoViewController, but name it ThreeViewController. Set the view to look like this:

2016-07-08_05-44-43

There’s no buttons here, so you have no actions to set. Change your simulator to iPhone 6s. Build and run. Tap the Next button and move between the three view controllers.

2016-07-06_08-25-30    2016-07-08_05-43-07    2016-07-08_05-43-43

Pushing a view controller from a  xib  is two lines of code:

let vc = ViewControllerName(nibName: "nameOfNib", bundle: nil)
navigationController?.pushViewController(vc, animated: true )

The first line creates the view controller. I tend to keep it simple and use vc, though if I had more than one, I’d be more descriptive.
Xibs are probably one of the most common uses for pushing a view controller. There are situations where you cannot use the storyboard and this is a good traditional alternative. Often self-contained reusable  modules will use a xib instead of a storyboard.

Programmatic Segues to View Controllers

For most uses, I prefer the storyboard over xibs for two reasons: first it is better documentation of the user interface. Secondly, I prefer to let the system do as much of the background work as possible by using the storyboard. The deeper into code you go, the more you have to worry about unexpected bugs.

We can programmatically push a view controller from a storyboard in two ways: segues or storyboard identifiers.  We’ll start with segues. One of the first ways anyone learns to use storyboards is direct segues, connecting up a button directly to a view. You can also do segues programmatically, which is useful when you conditionally go to a view.

Go to the storyboard. Add  two more view controllers to the storyboard. Label one View Controller Four and the other View Controller five.

2016-07-07_05-47-25

Click on the ViewController scene title in the storyboard. From the view controller Icon view controller iconon ViewController, control-drag from ViewController to somewhere on View Controller Four’s content so it highlights.

2016-07-07_05-55-32

Release the mouse button.  In the menu that appears, select Show.

2016-07-07_05-59-44

Click on the Show segue icon show segue icon to select the segue. Go into the properties inspector and set the segue’s Identifier to Four.

2016-07-07_06-03-18

Drag another button out to the View Controller scene and make the title Four.

2016-07-07_06-05-28

Go to ViewController class and add the following method:

@IBAction func fourFiveToggleButton(_ sender: UIButton){
    performSegue(withIdentifier: "Four",
        sender: self)
}

Open the assistant editor and drag from the circle next to the fourButton() method over the Four button and release.
The code above is a mere one line: it runs the segue. If you set the segue identifier correctly, that is all you need to do.

One use is conditional cases. Conditions in the current view controller or model might change. The app may display different view controllers based on those conditions. Let’s try a simple example. Just as we did with View Controller Four, make a segue with an identifier Five by control-dragging from the view controller icon view controller iconon View Controller One to the view of  View Controller Five.

2016-07-07_06-18-05

Select a Show segue. Select the segue by clicking the show segue icon show segue icon.  In the attributes inspector change the Identifier to Five.

Change the code for fourFiveToggleButton to this

@IBAction func fourFiveToggleButton(_ sender: UIButton){
    let normal = UIControlState(rawValue: 0) //beta 1 has no .normal bug#26856201
    if sender.titleLabel?.text == "Four"{
        performSegue(withIdentifier: "Four",
                     sender: self)
        sender.setTitle("Five", for: normal)
    } else{
        performSegue(withIdentifier: "Five",
                     sender: self)
        sender.setTitle("Four", for: normal)
    }
}

The code checks the contents of the title label. If the label is Four it goes to View Controller Four and changes the title label. If the label is Five, it goes to View Controller Five and toggles the label back to Four

Build and run.  we can toggle between the two views.

2016-07-07_06-44-06

An Interesting Stack Demonstration

Stacks are linear collections. How we compose that collection on the storyboard might be nonlinear. For example, View controllers four and five might both segue into view controller six.  On the storyboard drag out another view controller. Set a background color for it. Label it View Controller Six

 2016-07-08_05-55-47

For this example, we’ll use the storyboard directly. On View Controller Four, drag a Navigation Item. Title the navigation Item Four. Drag a bar button item to the navigation controller.  Title it Six

2016-07-08_05-57-26

Do the same for View Controller Five so the navigation bar looks like this:

2016-07-08_05-57-47

Control drag from View controller Four’s Six button to the Six View Controller. Select a Show Segue. Repeat for the  Five View Controller. Control drag from the Six button to the Six View Controller. Select a Show Segue.  Your storyboard now has this:

2016-07-08_06-06-24

We’ve set a storyboard where we go to Five or Four, and then go to six.  Build and run. We can go to Six from both view controllers.

2016-07-08_06-09-46

Closing a View Controller

Up to now, we’ve relied on the navigation controller’s Back button. Dismissal of view controllers with  popViewController() are common in an application. Almost every delegate between view controllers in a navigation stack will use it. There are several versions of the popping off controller for different uses.

Add two buttons to View Controller Six, titled Back and Root.

2016-07-08_06-14-23

Press Command-N to make a new class called SixViewController, subclassing UIViewController. Remove all the methods in the class.  In the SixViewController class, create an action  backButton:

 @IBAction func backButton(_ sender: UIButton) {
        navigationController?.popViewController(animated:true)
    }

You will get a warning  Expression of type 'UIViewController?' is unused. Ignore it for now, we’ll discuss it later.

Also in SixViewController,  add to the rootButton() method:

@IBAction func rootButton(_ sender: UIButton) {
  navigationController?.popToRootViewController(animated:true)
    }

Go to the story board and open the assistant editor. Drag from the circle next to backButton to the Back button. Drag from the circle next to the rootButton to the Root Button. Build and Run.  Go to Six. Press the new Back button. You go back to Four. Go to Six again and press the root button. You go back to One.

There are three versions of pop: popViewController(), popToRootController(), and popToViewController() The most Common is popViewController() which removes the top view controller from the stack. popToRootViewController() and popToViewController() pops everything or everything up to a specific view controller off the stack, returning what it popped off.

Because popViewController returns a value, we are getting the two warnings. We have to do something with the return value. You’ll notice in the error message that we get an optional returned.  If nil, there were no view controllers to pop off.  Use this to make sure you do have a navigation stack under the current controller. Change the class to this:

class SixViewController: UIViewController {
    @IBAction func backButton(_ sender: UIButton) {
        guard (navigationController?.popViewController(animated:true)) != nil
            else {
                print("No Navigation Controller")
                return
        }
    }
    @IBAction func rootButton(_ sender: UIButton) {
        guard navigationController?.popToRootViewController(animated: true) != nil
        else {
            print("No Navigation Controller")
            return
        }
    }
    
    }

By using guard and checking for nil the application checks to make sure there is a navigation stack. If not, code handles the error. If there is a segue set to Present modally by mistake and tries popping off the controller, the error gets handled. This is especially important in two situations: when you use a xib in a navigation controller and when you use a Storyboard ID. Both cases are independent of segues. Both can be used as a modal controller and a navigation controller. It’s likely you have modules set up for use in different applications, and sometime they are modal and sometimes navigation controllers. For example, go to the code for TwoViewController. Add the following action:

 @IBAction func backButton(_ sender:UIButton){
        guard navigationController?.popViewController(animated: true) != nil else { //modal
            print("Not a navigation Controller")
            dismiss(animated: true, completion: nil)
            return
        }
    }

We expanded the guard clause slightly here from the previous example. If there is no navigation controller, we must be in a modal. Instead of popping the controller, we dismiss it.

Add a Back button to the Two View Controller:

2016-07-08_06-58-13

With the assistant editor open, Control-drag from the Back button we created to the backButton code.

Add another button to View Controller One titled Two Modal.

2016-07-08_07-00-43

Open the assistant editor. Control drag the Two Modal Button to make a new action  named modalTwoButton. Add the following code to the new action to present a modal view:

@IBAction func modalTwoButton(_ sender: UIButton) {
    let vc = TwoViewController(
        nibName: "TwoViewController",
        bundle: nil)
    present(vc,
        animated: true,
        completion: nil)
    }

Build and run. Tap the TwoModal Button, and the modal view slides up from the bottom.

2016-07-08_07-23-36

Tap the Back button and it goes back to View Controller one. Tap the Next button  and you slide sideways into a navigation view.

2016-07-08_07-24-36

Tap Back and you are back in View Controller One

2016-07-08_07-27-12

Using Storyboard ID’s With Navigation Controllers

In between Xibs and the Storyboard are storyboard ID’s.  When you want all of your view controllers on one storyboard, but also want to call the view controller from several different controllers you might want to use a storyboard ID. Storyboard ID’s can programmatically called both by modals and navigation controllers. Some view controllers might have a segue at one place and called by a Storyboard ID in another.  On the storyboard find View Controller Six

2016-07-08_07-32-27

In the Identity inspector, set the Storyboard ID to Six

2016-07-08_07-34-45

On View Controller One add two more Buttons labeled Six Navigation and Six Modal.

2016-07-08_07-38-37

Control-Drag the Six Navigation Button into the assistant editor set to Automatic.  Make an action sixNavigationButton. Now do the same with the modal button. Control-Drag the Six Modal Button into the assistant editor.  Make an action sixModalButton.

The two actions are very similar. They will get a view controller from the method

 storyboard?.instantiateViewController(withIdentifier:String)

then present it for a modal or push it for a navigation controller. Add this code to the two actions:

@IBAction func sixNavigationButton(_ sender: UIButton) {
        guard let vc = storyboard?.instantiateViewController(withIdentifier: "Six") else {
            print("View controller Six not found")
            return
        }
        navigationController?.pushViewController(vc, animated: true)
    }
    
    @IBAction func sixModalButton(_ sender: UIButton) {
        guard let vc = storyboard?.instantiateViewController(withIdentifier: "Six") else {
            print("View controller Six not found")
            return
        }
        present(vc, animated: true, completion: nil)
    }

Go over to the SixViewController class. Add the dismiss method to dismiss the modal in the backButton action :

    @IBAction func backButton(_ sender: UIButton) {
        guard (navigationController?.popViewController(animated:true)) != nil
            else {
                dismiss(animated: true, completion: nil)
                return
        }
    }

Build and run. Tap Six Navigation, and you get to the navigation

2016-07-10_13-35-01

Tap back and you get back to One again. Tap six modal and you get the modal

2016-07-10_13-35-24

Tap back and you get back to One again.

2016-07-10_13-35-40

You’ll notice I left the Root button doing nothing for a modal since it has no meaning for modal.

One More Place to Explore: The Navigation Back Button.

For most cases we get a Back button like this on the navigation bar:

2016-07-08_08-08-37

But you may notice that the Six controller does this, depending where it is pushed from:

2016-07-08_08-07-53

2016-07-08_08-08-16

2016-07-08_08-11-38

The title for the Back button comes from the view controller below it on the stack. When I push Six from Five, Five shows up as the title in the back button. This is another exploration you might want to take about navigation controllers, which you can find in the post Using the Navigation Bar Title and Back Button

The Whole Code

<h2>ViewController.swift</h2>
//
//  ViewController.swift
//  SwiftProgNavControllerDemo
//
//  Created by Steven Lipton on 7/6/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import UIKit

class ViewController: UIViewController {
//    
// Action for using a story board id for navigation controller
//  let vc = storyboard?.instantiateViewController(withIdentifier: "Six")
//
    @IBAction func sixNavigationButton(_ sender: UIButton) {
        guard let vc = storyboard?.instantiateViewController(withIdentifier: "Six") else {
            print("View controller Six not found")
            return
        }
        navigationController?.pushViewController(vc, animated: true)
    }
    
//    
// Action for using a story board id for modal controller
//  let vc = storyboard?.instantiateViewController(withIdentifier: "Six")
//
    @IBAction func sixModalButton(_ sender: UIButton) {
        guard let vc = storyboard?.instantiateViewController(withIdentifier: "Six") else {
            print("View controller Six not found")
            return
        }
        present(vc, animated: true, completion: nil)
    }
    
//
// modal example for use with dismissal see TwoViewController
//
    @IBAction func modalTwoButton(_ sender: UIButton) {
        let vc = TwoViewController(
            nibName: "TwoViewController",
            bundle: nil)
        present(vc, animated: true, completion: nil)
    }

//
// Example of pushing a view controller 
//navigationController?.pushViewController(vc, animated: true)
//
    @IBAction func nextButton(_ sender: UIButton) {
        let vc = TwoViewController(
            nibName: "TwoViewController",
            bundle: nil)
        navigationController?.pushViewController(vc, animated: true)
    }

//
// Example of Logically pushing a view controller from a segue
// performSegue(withIdentifier: "Four",sender: self)
//
//
    
    @IBAction func fourFiveToggleButton(_ sender: UIButton){
        let normal = UIControlState(rawValue: 0) //beta 1 has no .normal bug#26856201
        if sender.titleLabel?.text == "Four"{
            performSegue(withIdentifier: "Four",
                         sender: self)
            sender.setTitle("Five", for: normal)
        } else{
            performSegue(withIdentifier: "Five",
                         sender: self)
            sender.setTitle("Four", for: normal)
        }
    }

}




<h2>TwoViewController.swift</h2>




//
//  TwoViewController.swift
//  SwiftProgNavControllerDemo
//
//  Created by Steven Lipton on 7/6/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import UIKit

class TwoViewController: UIViewController {

//
// A back button for dismissal of both modals and navigation controllers
//
//
    @IBAction func backButton(_ sender:UIButton){
        guard navigationController?.popViewController(animated: true) != nil else { //modal
            print("Not a navigation Controller")
            dismiss(animated: true, completion: nil)
            return
        }
    }
}




<h2>ThreeViewController.swift</h2>




//
//  ThreeViewController.swift
//  SwiftProgNavControllerDemo
//
//  Created by Steven Lipton on 7/6/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import UIKit

class ThreeViewController: UIViewController {
    }





<h2>SixViewController.swift</h2>




//
//  SixViewController.swift
//  SwiftProgNavControllerDemo
//
//  Created by Steven Lipton on 7/8/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import UIKit

class SixViewController: UIViewController {
 
//
// Another back button for dismissal of both modals and navigation controllers
//
//   
    @IBAction func backButton(_ sender: UIButton) {
        guard (navigationController?.popViewController(animated:true)) != nil
            else {
                dismiss(animated: true, completion: nil)
                return
        }
    }

//
// A pop to root of the navigation controller example 
//  navigationController?.popToRootViewController(animated: true)
//
    @IBAction func rootButton(_ sender: UIButton) {
        guard navigationController?.popToRootViewController(animated: true) != nil
        else {
            print("No Navigation Controller")
            return
        }
    }
    
    }


Why Does Apple Need Delegates?

In my post Why do we need delegates, I explained one major developer use of delegates: moving data from a destination view controller back to the controller that presented it or pushed it onto the stack. This is not the only use of delegates. Apple uses it often  in APIs. In this theoretical lesson we’ll discuss what they doing under-the-hood and why. We’ll dissect the entire process, so you will understand what you are doing in your code.

The Basic Problem

Let’s start with a simple class, which if you want you can copy into a playground to try:

class SimpleClass{
    var array:[String] = ["A","B","C","D"]
    func printArray(){
        for string in array {
            if string.characters.count != 0{
                print(string)
            }else{
                print("Empty String")
            }
        }
    }
}

We use this class this way:

let simpleClass = SimpleClass()
simpleClass.printArray()

This class prints an array. When a developer writes this you have access to the methods and functions. If you wanted to re-write this to be more customizable, you could just change the source. You can assign different values to the array.
However, Apple (and sometimes developers) don’t want to you messing with their code. Apple will suspend your developer license for doing that to an API. Often Apple writes the code so you have no access to things like array at all. Suppose our code changes to this:

class SimpleClass{
    func printArray(){
        let array:[String] = ["A","B","C","D"]
        for string in array {
            if string.characters.count != 0{
                print(string)
            }else{
                print("Empty String")
            }
        }
    }
}

Here’s the problem: You want to format string and set the values in array. You just can’t since you have no access to them.

Adding a Protocol

What Apple does for these customization situations is write protocols for the class. You’ll find two different names of this protocol. For getting data, you’ll find a protocol called a data source. For changing data or reacting to events, you’ll find a delegate.

Adding a Data Source

A data source is a function that defines the data in a class. You write the function to return the data you want to use. For example, we could add the following protocol for array

protocol SimpleClassDataSource{
    func array() -> [String]
}

We could then change our code to this:

class SimpleClass{
    var dataSource:SimpleClassDataSource! = nil
    func printArray(){
        let array:[String] = dataSource.array()
        for string in array {
            if string.characters.count != 0{
                print(string)
            }else{
                print("Empty String")
            }
        }
    }
}

Line 2 creates a property dataSource which has an optional value of SimpleClassDataSource as its type. In line 4, the code calls dataSource function array to get the data for the array.

Of course, there is no data yet. That’s in the class that will use SimpleClass and adopts the SimpleClassDataSource protocol. Suppose we had this class:

class PrintingDoughnuts{
}

We could use our simple class to print the doughnuts. First, add a constant simpleClass, and an initializer to run it.

class PrintingDoughnuts{
    let simpleClass = SimpleClass()
    
    init() {
        simpleClass.printArray()
    }
}

If we try running this code, with PrintingDoughnuts() we get an error:
fatal error: unexpectedly found nil while unwrapping an Optional value

The protocol is set to nil, and thus crashes. We still need to assign the data we want. First, we adopt the protocol,

class PrintingDoughnuts:SimpleClassDataSource{
    let simpleClass = SimpleClass()
    
    init() {
        simpleClass.printArray()
    }  
}

Then we tell simpleClass to find our implementation of array inside PrintingDoughnuts, by assigning self to the data source.

class PrintingDoughnuts:SimpleClassDataSource{
    let simpleClass = SimpleClass()
    
    init() {
        simpleClass.dataSource = self
        simpleClass.printArray()
    }
}

Finally, we implement our version of array

class PrintingDoughnuts:SimpleClassDataSource{
    let simpleClass = SimpleClass()
    
    init() {
        simpleClass.dataSource = self
        simpleClass.printArray()
    }
    
    func array() -> [String] {
        return ["Boston Cream","Cruller","Old Fashioned","","Frosted"]
    }
}

I just assigned a literal array here, but I could have just as easily called an external XML File to download data, or calculated some values. I can do anything here that returns a string array. That’s the power of a data source: While the class does not change I can do anything I want with it.

If I go and run this code by PrintingDoughnuts(), I get this output:

Boston Cream
Cruller
Old Fashioned
Empty String
Frosted

You can think of data sources setting properties otherwise inaccessible by coding, because someone made it impossible to get at the property directly.

Adding Delegates

While data sources are for properties, delegates are for methods. If you want to do something because of a change in your values or with your data you find those in a delegate. The developer of SimpleClass for example might use a delegate to change string and to react to a blank string in the array. The protocol for that might look like this:

protocol SimpleClassDelegate{
    func format(string:String)->String
    func handleEmptyString()
}

The SimpleClass would use it like this:

class SimpleClass{
    var dataSource:SimpleClassDataSource! = nil
    var delegate:SimpleClassDelegate! = nil
    func printArray(){
        let array:[String] = dataSource.array()
        for string in array {
            if string.characters.count != 0{
                print(delegate.format(string: string))
            }else{
                delegate.handleEmptyString()
            }
        }
    }
}

Line two creates an optional property called delegate with type SimpleClassDelegate and sets it to nil. Lines 8 and 10 use the methods of delegate. Again we don’t know what those methods are yet. That happens in the class that adopts the protocol.

Just like the data source, we’d implement the adoption of the protocol like this:

class PrintingDoughnuts:SimpleClassDataSource, SimpleClassDelegate{
    let simpleClass = SimpleClass()
    
    init() {
        simpleClass.dataSource = self
        simpleClass.delegate = self
        simpleClass.printArray()
    }
    // data source
    func array() -> [String] {
        return ["Boston Cream","Cruller","Old Fashioned","","Frosted"]
    }
    // delegates for self
    func format(string: String) -> String {
        return "Yummy " + string + " Doughnut"
    }
    func handleEmptyString() {
        print("Waaah!! No Doughnut!")
    }
}

Like the data source, adopt the protocol in line 1. Tell delegate in line 6 that the delegate methods can be found in this class. Lines 13 through 19 are those delegate methods.

Our output changes:

Yummy Boston Cream Doughnut
Yummy Cruller Doughnut
Yummy Old Fashioned Doughnut
Waaah!! No Doughnut!
Yummy Frosted Doughnut

External Implementation of a Protocol

One more thing about line 5 and 6. We set dataSource and delegate to self. In most cases, this is what you will do, because you want to write the required methods in the same class as you use them. However, this is not always the case. If you will use the same methods for multiple classes you may make a separate class like this:

class DoughnutDelegateMethods:SimpleClassDelegate{
    // delegates
    func format(string: String) -> String {
        return "Very Yummy " + string + " Doughnut"
    }
    func handleEmptyString() {
        print("**Waaah!! No Doughnut!**")
    }
}

We’ve adopted the protocol, and implement the required methods in this separate class with a few changes to tell the difference.
We can change line 6 of PrintingDoughnuts to this:

simpleClass.delegate = DoughnutDelegateMethods

Now the delegate is reading the methods from the DoughnutDelegateMethods class:

Very Yummy Boston Cream Doughnut
Very Yummy Cruller Doughnut
Very Yummy Old Fashioned Doughnut
**Waaah!! No Doughnut!**
Very Yummy Frosted Doughnut

We can now use the same delegate method in another class for Firecakes Doughnuts, an artisan doughnut shop in Chicago:

class PrintingFirecakes:SimpleClassDataSource{
    let simpleClass = SimpleClass()
    
    init() {
        simpleClass.dataSource = self
        simpleClass.delegate = DoughnutDelegateMethods()
        simpleClass.printArray()
    }
    // data source
    func array() -> [String] {
        return ["Triple Valrhona Chocolate Cake","Maple Glazed Pineapple & Bacon","Butterscotch Praline","","Malted Milk Ball"]
    }
}

This time we only adopted the data source.We only need to adopt the delegate when writing the delegate methods. For our delegate we told the system to find it in the class DoughnutDelegateMethods. Since they are not in this class we don’t adopt them. Running the PrintingFirecakes() in a playground gets us a list of these doughnuts too:

Very Yummy Triple Valrhona Chocolate Cake Doughnut
Very Yummy Maple Glazed Pineapple & Bacon Doughnut
Very Yummy Butterscotch Praline Doughnut
**Waaah!! No Doughnut!**
Very Yummy Malted Milk Ball Doughnut

How Apple’s API’s Use This

With that look under the hood of API delegates and data sources, we can look at what you need to do for any API that has delegates. Some have required delegates, such as the UITableView, UIImagePickerController and CLCoreLocationManager classes. In Swift, UITableview's delegate and data source are so important, Apple bundled them into the Swift version of the class, while you do have to adopt them in Objective-C.

In a non working example lets look at CLCoreLocationManager. For an API delegate or data source we have three things to do

  1. Adopt the delegate
  2. Set the location of the delegate’s method implementation, usually self to say “within this class”
  3. Implement the delegate methods

Here’s a fragment of code to show what it would look like. By now you should understand what Apple is doing. All of the methods that return values from the GPS are delegate methods found in CLLocationManagerDelegate. When one of those events occurs, that delegate method gets called. If you want to do something at that event you implement that delegate method.

/*
//
// This shoud really be run on a device, not the playground
// commented out as a non working example, which is missing functional parts
 
import MapKit
// #1- adopt the delegate
class WhereAmI:NSObject,CLLocationManagerDelegate{
let myLocationManager = CLLocationManager()
    override init(){
        super.init()
 
 // #2 - set the delegate implementation to this class
        myLocationManager.delegate = self      
    }
 //#3 - Use delegate functions
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        let myLocation = locations.first
        print ("my latitude = \(myLocation?.coordinate.latitude)")
        print ("my longitude = \(myLocation?.coordinate.longitude)")
    }
}

WhereAmI()
*/

The Whole Code

This was an under the hood explanation of something developers use often. You still might want to play with the code I wrote. You can cut and paste the code below into an Apple playground, or click here to run the code in the IBM swift sandbox.

//:
//: An example of API use of delegates and data sources
//: (c)Steven Lipton (makeapppie.com) July 2016
//: See http://makeapppie.com for explanation

import UIKit
//
//: A data source protocol
//
protocol SimpleClassDataSource{
    func array() -> [String]
}
//
//: A delegate protocol
//
protocol SimpleClassDelegate{
    func format(string:String)->String
    func handleEmptyString()
}
//
//:A simple class that uses the protocols 
//
class SimpleClass{
    var dataSource:SimpleClassDataSource! = nil
    var delegate:SimpleClassDelegate! = nil
    func printArray(){
        let array:[String] = dataSource.array()
        for string in array {
            if string.characters.count != 0{
                print(delegate.format(string: string))
            }else{
                delegate.handleEmptyString()
            }
        }
    }
}
//
//: The first example class that uses SimpleClass
//
class PrintingDoughnuts:SimpleClassDataSource, SimpleClassDelegate{
    let simpleClass = SimpleClass()
    
    init() {
    //If we want to implement the protocol in this class, we use self
        simpleClass.dataSource = self
        simpleClass.delegate = self
    //If we want to implement the protocol in an external class
        //simpleClass.delegate = DoughnutDelegateMethods()
        simpleClass.printArray()
    }
    // data source implementation
    func array() -> [String] {
        return ["Boston Cream","Cruller","Old Fashioned","","Frosted"]
    }
    // delegate implementations
    func format(string: String) -> String {
        return "Yummy " + string + " Doughnut"
    }
    func handleEmptyString() {
        print("Waaah!! No Doughnut!")
    }
}
//
//: An external implementaion of the delegate methods, used
//: when sharing the implementaion between classes
//
class DoughnutDelegateMethods:SimpleClassDelegate{
    // delegates
    func format(string: String) -> String {
        return "Very Yummy " + string + " Doughnut"
    }
    func handleEmptyString() {
        print("**Waaah!! No Doughnut!**")
    }
}
//
//: An eaxmple of a second method sharing the delegate implementation
//
class PrintingFirecakes:SimpleClassDataSource{
    let simpleClass = SimpleClass()
    
    init() {
        simpleClass.dataSource = self
        simpleClass.delegate = DoughnutDelegateMethods()
        simpleClass.printArray()
    }
    // data source
    func array() -> [String] {
        return ["Triple Valrhona Chocolate Cake","Maple Glazed Pineapple & Bacon","Butterscotch Praline","","Malted Milk Ball"]
    }
}

 let doughnutList = PrintingDoughnuts()
 let fireCakeList = PrintingFirecakes()
/*
 
//
//: An example of CLLocationManager using delegates 
//: This shoud really be run on a device, not the playground
//: commented out as a non working example, which is missing functional parts
// 
import MapKit
//: #1- adopt the delegate
class WhereAmI:NSObject,CLLocationManagerDelegate{
let myLocationManager = CLLocationManager()
    override init(){
        super.init()
 
 //: #2 - set the delegate implementation to this class
        myLocationManager.delegate = self      
    }
 //: #3 - implement delegate functions
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        let myLocation = locations.first
        print ("my latitude = \(myLocation?.coordinate.latitude)")
        print ("my longitude = \(myLocation?.coordinate.longitude)")
    }
}

let whereAmI = WhereAmI()
*/

Adventures in Swift and iOS App Development

Follow

Get every new post delivered to your Inbox.

Join 639 other followers