Passing data with the protocol delegate pattern

Delegates are the standard way we pass data between two view controllers. I recently wrote a tutorial on how to pass data by overriding prepareForSegue. In this tutorial I’ll show you how to pass messages back and forth using the “Standard” protocol pattern and without using any segues.

There won’t be an in-depth discussion about protocols or delegates here, for that I defer to Apples Swift Programming Language Guide or one of the many excellent books or online resources that have already covered this topic ad nauseam. 

What I will do, is create a succinct-minimal-working delegate pattern example, using naming conventions that will make it clear how the delegate pattern works. 

Setting Up

We’ll start by creating a single view application.

We’ll add a second View Controller to our storyboard which we’ll need to create a new CocoaTouch file for.  We’ll do that by clicking add new > cocoa touch class >  find UITableViewController… name it PresentedTableViewController and create. 

This controller class will belong to our “presented” view controller, so make sure you add it in your storyboard. Your final storyboard should look like this:

In the first View Controller, lets add a button and a label. We don’t need anything fancy for this exercise, just slap em out there. With split view open, control drag an outlet from your label and name it labelOutlet, and control drag an action from your button and name it doPresent. You should now have something like this:

    
class ViewController: UIViewController {
    
    @IBOutlet weak var textOutlet: UILabel!
    @IBAction func doPresent(sender: AnyObject) {

    }
    
    override func viewDidLoad() {
        super.viewDidLoad()   
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

With an interface that looks like this.

Now, select the other View Controller, the PresentedViewController and add a Text Field and a Button to the View. Just as before, control drag from your Text Field and create an outlet named “textFieldOutlet” and repeat that for your button, this time making an action named “doDismiss”. Your code should look like this:

    
class PresentedViewController: UIViewController {
   
    @IBOutlet weak var textFieldOutlet: UITextField!
    @IBAction func doDismiss(sender: AnyObject) {

    }
    
    override func viewDidLoad() {
        super.viewDidLoad()

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

And your interface should resemble mine:

Protocol

The Standard pattern is to add our protocol to top of our “presented” class, though I’ve seen people create separate files for these too. We’ll do it the standard way here.

At the top of your PresentedViewController class, just above your class declaration we’ll add the following code.

    
protocol PresentedViewControllerDelegate {
    func acceptData(data: AnyObject!)
}

The convention is to adopt the presented classes name with “Delegate” afterwords. Inside our protocol we’ve defined one function, acceptData that takes AnyObject. I should note that we can define any number of functions and properties inside of our protocol, but no logic. 

Think of a protocol as a binding contract between the protocol and any class that implements it. This contract forces said classes to abide by it's "protocol" and in doing so creates a level of "trust" from the compiler. This "trust" enables us to communicate between two separate bodies of code.

PresentedViewController

We’ll add the required code necessary to receive data from the view controller, but this wont be a one way dialogue. We’ll wire this up so it will send data back to the View Controller as well.

At the class level, add two properties. One being a variable called delegate with a type PresentedViewControllerDellegate. Which will look like this:

    
// create a variable that will recieve / send messages
// between the view controllers.
var delegate : PresentedViewControllerDelegate?
// another data outlet
var data : AnyObject?

We’ll add a print statement in our viewDidLoad that will receive our “data” object from the ViewController. 

    
 override func viewDidLoad() {
    super.viewDidLoad()
    print("\(data!)")

}

Now, we’ll handle our Text Field and Button by adding the following lines of code inside the doDismiss method:

    
@IBAction func doDismiss(sender: AnyObject) {
    if textFieldOutlet.text != "" {
        self.presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
    }
}

You’ll notice that I’ve added a simple check to validate that the text field has content before we call presentingViewController!.disissViewControllerAnimated.

Finally, we’ll add viewWillDisappear which will  send our Text Field data to our delegates acceptData method. 

    
override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    if self.isBeingDismissed() {
        self.delegate?.acceptData(textFieldOutlet.text)
    }
}

Back in our ViewController, we’ll add PresentedViewControllerDelegate to our class signature like this:

    
class ViewController: UIViewController, PresentedViewControllerDelegate {//...

We’ll proceed with our protocol implementation by adding its acceptData(data:AnyObject!) method, which silences the compiler. Inside that method we’ll simply update our text label with the message we entered in PresentedViewController.

    
func acceptData(data: AnyObject!) {
    self.textOutlet.text = "\(data!)"
}

Finally, we’ll address the doPresent action we created earlier. Since in this exercise we aren’t passing values via Segue, we’ll need a method of traversing our data. In this case, it’s by presenting the view controller. We’ll create an instance of our PresentedViewController and with that instance, we can bind to it properties before presenting. That code looks like this:

    
@IBAction func doPresent(sender: AnyObject) {
    let pvc = storyboard?.instantiateViewControllerWithIdentifier("PresentedViewController") as! PresentedViewController
    pvc.data = "important data sent via delegate!"
    pvc.delegate = self
    self.presentViewController(pvc, animated: true, completion: nil)
}

Build And Run

When click the next button you should see the next view controller along with a print line in your console. If you enter some text in the text field and hit your go back button, you’ll see the text you entered assigned to you label.

If you have any troubles, feel free to download the Project Files and browse my code. You can also ask questions in the comments and we’ll get your code running.