The Swift Swift Tutorial: Using Segues and Delegates in Navigation Controllers (Part 1 — The Template)

[Updating to Swift 2.0 SJL 9/17/15]

Click here for the Swift 3.0 version of this post

It should be one of the easiest things we do, and yet for many it is the most confusing. Getting data from one view controller to another as you switch views never seems easy. Segues might be slightly difficult, but delegates to get the information back have given many a developer a headache or two. Swift does streamline the process a bit, but much of the setup is the same as Objective-C. Today we’ll discuss this often used technique.

As this can get a bit convoluted, I decided to break this topic down into two posts. This post will make a new very tiny app which is nothing but a segue, a delegate, and a few controls to prove they work. The next post we will add all this to the pizza demo app, and discuss more practical issues with using delegates. For those looking for factory delegates, we’ll discuss that in two posts from now when I discuss something that uses a lot of them: UITableViews .

Make a New App

The First Scene

Make a new single-view Swift project named Swift2DelegateFoo . In the storyboard click on the view controller, and embed it in a navigation controller by selecting Editor>Embed In> Navigation Controller. Add a label in the center of the view, centered and with the text Unknown Color . Drag a bar button item to the right side of the navigation bar. Change its title to Color . It should look something like this.

In the view controller, remove everything but the viewDidLoad() method. Using the assistant editor, connect an outlet for the UILabel called colorLabel . Your code should look like this:

import UIKit class ViewController: UIViewController { @IBOutlet var colorLabel : UILabel! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. } }

The Second Scene

Make a new Cocoa Touch class that subclasses UIViewController from File>New>File…. Call it FooTwoViewController . In the code, remove everything but the viewDidLoad() method.

Go back to the storyboard and drag in another View controller. From the Foo One Navigation bar, control-drag the Color bar button item to the new controller to connect the segue. Set the segue to show and in the property inspector set the identifier to mySegue . Select the new view controller again in the storyboard. Once we’ve set the segue, in the identity inspector set the custom class for this view controller to be FooTwoViewController .

Now we’ll add a label, a navigation item, a bar button item, and three buttons. First add the label and place it towards the center of the view. Make the label the width of the view. Place the three buttons under the label. Title the buttons Red , Green and Blue respectively. Drag a navigation item into the view and in the label, title it Foo Two . Finally add the bar button item to the navigation bar. Your finished view should look like this:

Open the assistant editor if not already open. Control-drag the label into the FooTwoViewController class. Create an outlet named colorLabel . Select the bar button, and control-drag it to the class code. Make an action named saveColor . Select the red, green and blue buttons, and control drag them into FooTwoViewController . Make an action for a UIButton called colorSelectionButton . In the colorSelectionButton method add this code:

colorLabel.text = sender.titleLabel!.text!

Above the colorLabel outlet add a string variable colorString :

var colorString = ""

Finally, add this to viewDidLoad() to set the label correctly from the property:

colorLabel.text = colorString

Your code for FooTwoViewController should look like this:

import UIKit class FooTwoViewController: UIViewController { var colorString = "" @IBOutlet var colorLabel : UILabel! @IBAction func saveColor(sender : UIBarButtonItem) { } @IBAction func colorSelectionButton(sender: UIButton) { colorLabel.text = sender.titleLabel!.text! } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. colorLabel.text = colorString } }

Build and Run. Go back and forth between the two views.

Adding prepareForSegue

To move data from Foo One to Foo Two, we will override prepareForSegue() . Overridden methods in the editor are a bit tricky at first, since the auto fill-in has a new feature. In Swift a method that is overridden starts with the keyword override , as you can see with the viewDidload method above. However when typing in any defined method getting overridden, you don’t start by typing override . Instead you type the function or method name and Xcode adds override func to it. Go into the viewController.swift file and under the viewdidload method but before the end of the class, type prepareforS . Xcode will automatically populate the rest for you. Press tab, or click the selection on the drop down. Add braces to the end so you have this:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) { }

Add the following code to this method so the prepareForSegue() reads:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) { if segue.identifier == "mySegue"{ let vc = segue.destinationViewController as! FooTwoViewController vc.colorString = colorLabel.text } }

In line 1, the first parameter is segue , which is a UIStoryboardSegue object. This is the segue we call for our transition between controllers. The exclamation point means that it is not an optional value, and we can use it as is (see below for more on optional values). In line 2 we check to see if this is the correct segue. Line 3 we create vc which is the pointer to the segue’s destination view controller. destinationviewcontroller is of type AnyObject! a Swift equivalent to Objective-C’s id . We need to cast, or more technically downcast to the correct object type with the as operator, and make vc an instance of FooTwoController . Once we do that, we can access the colorString property in line 4, sending our current text to the property in the new controller.

Build and Run. We now can send “Unknown Color” to the new controller.

Now comes the slightly more challenging part: getting stuff back. For that we will need a delegate.

Protocols and Delegates in Swift

Like Objective-C, delegation uses protocols. Protocols for those who are not familiar are properties and methods that while declared in one place, another class implements. They allow for a layer of abstraction. A class adopts a protocol to do something. The protocol defines what the something is. The adopting class will have the code how it gets done.

We are going to set up a protocol to dismiss the Foo Two controller, and then adopt it in the main view controller. This way we can indirectly access properties of the Foo Two controller. Such an access is a delegate. In the Foo Two controller above the class definition for FooTwoViewController , add the following:

protocol FooTwoViewControllerDelegate{ func myVCDidFinish(controller:FooTwoViewController,text:String) }

This sets up the protocol FooTwoViewControllerDelegate with a required method myVCDidFinish() . I used a incredibly generic name here, you can of course make it more specific for readability. Next, in the FooTwoViewController class, add the following just after the class definition:

var delegate:FooTwoViewControllerDelegate? = nil

We define a delegate of optional type FooTwoViewControllerDelegate and set its value to nil . Although I covered optional values last time, they are so important I’ll review this important part of Swift. We are familiar with simple values such as a Double or Int . If we want a state for any object that has a nil state, we use an optional value. Declare an optional with a question mark after the type, as it is above. An Optional value of a Double? can be nil , along with any number for example. There is a cost to this extra power. To get back that number you have to unwrap the optional value with the ! operator. The ! operator will return a value as long as the optional is non-nil. If nil , it will break program execution with a run-time error. Before using a ! operator almost always check for nil . For example, add the last part of our code to the saveColor method:

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

The if statement in line 2 tests an optional for nil. In the code above we first test our delegate, then unwrap the delegate to use the myVCDidFinish method. As a protocol, it isn’t defined here. The adopting class defines it, so our next stop is adopting the protocol back at the original view controller.

Open the ViewController.swift file. Change the class definition to:

class ViewController: UIViewController,FooTwoViewControllerDelegate

You should immeiately get the expected error:

Type ViewController does not conform to protocol FooTwoViewControllerDelegate

We need to write the protocol. On a blank line just above prepareForSegue() , start typing myVCDidFinish until auto complete finds it, press tab, add braces and the rest of this:

func myVCDidFinish(controller: FooTwoViewController, text: String) { colorLabel.text = "The Color is " + text controller.navigationController?.popViewControllerAnimated(true) }

We take the value of text and place it in our label. We then use the controller to dismiss the controller with popViewControllerAnimated . Note that in Swift, there is no YES and NO but true and false for Boolean values.

We need to set up the delegate in the segue. Add to prepareForSegue() , just under colorString = colorLabel.text :

vc.delegate = self

Build and run. You should be able to send “unknown” to Foo Two. Click a color and send the color back.

This is the basic segue and delegate template, which you can try to use on your own projects. Next time We’ll add it to the Pizza Demo app and edit the dictionary.

If you are not uderstand what we did here, you might need a little help with a few few concepts of delegation. this is mostly how, not why. For the why, read Why Do We Need Delegates

The Whole Code

Here is the code for the viewController.swift and FooTwoViewController so you can see everything in context.

// // ViewController.swift // Swift2DelegateFoo // // Created by Steven Lipton on 6/29/14. // Copyright (c) 2014 Steven Lipton. All rights reserved. // Updated 9/17/15 SJL for Swift 2.0 import UIKit class ViewController: UIViewController,FooTwoViewControllerDelegate { @IBOutlet var colorLabel : UILabel! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. } func myVCDidFinish(controller: FooTwoViewController, text: String) { colorLabel.text = "The Color is " + text controller.navigationController?.popViewControllerAnimated(true) } override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) { if segue.identifier == "mySegue"{ let vc = segue.destinationViewController as! FooTwoViewController vc.colorString = colorLabel.text! vc.delegate = self } } }

// // FooTwoViewController.swift // Swift2DelegateFoo // // Created by Steven Lipton on 6/29/14. // Copyright (c) 2014 Steven Lipton. All rights reserved. // updated 9/17/15 SJL for Swift 2.0 import UIKit protocol FooTwoViewControllerDelegate{ func myVCDidFinish(controller:FooTwoViewController,text:String) } class FooTwoViewController: UIViewController { var delegate:FooTwoViewControllerDelegate? = nil var colorString = "" @IBOutlet var colorLabel : UILabel! @IBAction func saveColor(sender : UIBarButtonItem) { if (delegate != nil) { delegate!.myVCDidFinish(self, text: colorLabel!.text!) } } @IBAction func colorSelectionButton(sender: UIButton) { colorLabel.text = sender.titleLabel!.text! } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. colorLabel.text = colorString } }