Training and Instructional Design
[Updated to Swift 2.0/iOS9 SJL 9/28/15]
A Note to readers: This post is out of date. Since Swift 3.0, iOS 10 and watchOS 3 have so many changes, I made a new post wth proper syntax, and a lot better illustrations. You can find that here on my site or here on LinkedIn Pulse
A reader in a comment recently asked a very good question: Why do we need delegates for UIViewControllers? He thought Swift made things easier, but this delegate stuff seems very complicated. Shouldn’t we be able to send a message or initializer between classes?
When I first learned iOS, I’ll admit it took me months to understand what happened with delegation. I found lots of confusing code and little explanation. Moving away from my usually intermediate-level coding, I’m going to go back to basic computer science and break down that question in detail. I will be working in Swift for examples but the concepts should apply to every application developed in Objective-C, C++, Java, Python or any other Object-oriented language you can think of.
While we use classes in object-oriented programming, it’s good to review what they actually are. 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 classes 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, there is also a default state which 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. In general, it is best to leave what is public by other classes 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 Legos.
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.
A 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 the major parts of an application. First it seperates 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 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.
There 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 or does it ask for input. It is just data. Here is an example of a model made from two classes:
class Pizza{ var pizzaTopping:String var pizzaCrust:String var pizzaSize:Double var pizzaRadius:Double { get{ return pizzaSize / 2.0 } } let pi = 3.1415926 let crust = 0.1 init(topping:String,crust:String,size:Double){ pizzaTopping = topping pizzaCrust = crust pizzaSize = size } func howMuchCheese(height:Double) -> Double{ return (pizzaRadius * pi * (1.0 - crust )) * height } } class PizzaOrder{ private var pizzaArray:[Pizza] = [] private var tableNumber:[Int] = [] func addOrder(pizza:Pizza, table:Int){ pizzaArray.append(pizza) tableNumber.append(table) } func getOrder(index:Int) -> (Pizza,Int){ return (pizzaArray[index],tableNumber[index]) } func getPizzaOrder(index:Int) -> Pizza{ return pizzaArray[index] } func changePizzaOrder(index:Int, newPizza:Pizza){ pizzaArray[index] = newPizza } }
This model is the data for a basic pizza ordering system. An instance of class PizzaOrder
will be a list of pizzas of class Pizza
, and a list of table numbers. There is a lot more methods I should add to describe the pizza ordering process, but like addOrder()
and getOrder()
any method is moving data only. There is no user input or output here. They might make calculations as we do in the howMuchCheese()
method, but again there is no user interaction here.
Where all the user interaction happens is in the view. In Xcode, most people use Interface Builder either as a scene in a storyboard or a .xib file to build their views. A developer can 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 data. The model doesn’t do much but sit there. It might respond to a user touch with feedback such as a notifying a method somewhere, a color change when a button 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. it can’t do anything about it, but it can broadcast something.
The 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 containing views to the view controller.
The key to MVC is communication. To be more accurate, the lack of communication. 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 view and controller may do an internal action to the message sent as a method call or it may return a value to the controller. The controller never directly changes anything in either the view or the model.
As an aside we need to talk about how Xcode with @synthesize
and 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 pizzaSize
might be written this way:
private var pizzaSize:Double = 0.0 func setPizzaSize(size:Double){ pizzaSize = size } func pizzaSize()->Double{ return pizzaSize }
We would have to do that for every property, which gets a bit tedious. Apple introduced @synthesize
in Xcode 4.4 for Objective-C to automate this process. Instead of writing your setters and getters, all that was necessary was to add a statement in your implementation file
@synthesize pizzaSize
Xcode then generates the setter and getter behind the scenes for you. Later versions of Objective-C and Swift hide this completely and create the illusion you are directly modifying a property. You don’t need the setter or getter (though you can write your own) and in Swift @synthesize
is not even in the language since it hides the setter and getter completely unless you specify it.
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 method, often 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.
What we’ve discussed so far is for only one scene in a much larger application. Suppose I have the following storyboard:
I have an Edit Pizza scene in my pizza order entry app. I need to send information to it to run properly and I would need to get information back. In the world of MVC, the controller does this. But we’ve a small problem: Data flows one-way from the calling controller to the new controller.
In Xcode we have what are known as segues
. Segues are a convenience to point from one view controller to another. There are alternates to segues including initializing the new view controller from the old one directly. This is useful for popovers and modal views. When working with more complex navigation controllers segues keep track of some things that would become cumbersome to control ourselves in a simple way. When we move from one controller to the next, the segue tells the system to open this particular view controller, which then opens up a view and model. The model and view in the new MVC setup is different then the calling one. Apple includes a method prepareForSegue()
which give us a chance to set values in the new view controller, and subsequently the new view controller’s view and model.
I might for example edit a pizza order. The user would give the order number, and the controller would ask the model for the pizza associated with that order. The model would return that order as an instance of Pizza
and send that to a new view controller whose model is a single instance of pizza. Our prepareForSegue()
for a navigation controller might look like this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!){ if segue.identifier == "editOrder" { var vc = segue.destinationViewController as! EditPizzaViewController let orderNumber = Int(orderNumberText.text!) vc.pizza = pizzaOrder.getPizzaOrder(orderNumber) vc.orderNumber = orderNumber } }
We first find if this is the correct segue in line 2. If so, we make the new view controller EditPizzaViewController
. We get an order number from the text field in line 4. We set two properties, orderNumber
, and the pizza
which becomes the model for the new view controller. Everything moves nicely and when the Edit Pizza screen opens, everything looks good.
We can edit the information for the pizza easily enough in the new MVC. But when we press Done to send it back to the OrderPizzaViewController
is when problems show up. By the rules of MVC, we need a method to return a value. Where in a called instance can we go back to the class calling it? With an encapsulated class we can’t. There is no way to send that revised model back to the original controller without breaking encapsulation or MVC. The new view controller does not know anything about the class that called it. We look stuck. If we try to make a reference directly to the calling controller, we may cause a reference loop that will kill our memory. Simply put, we can’t send things backwards.
This is the problem that delegates and protocols solve by being a little sneaky. Imagine another class, one that is really a skeleton of a class. This class contains only methods. It declares that certain methods are in this class, but never implements them. Generally known as abstract classes, in Swift they are protocols. We make a protocol class that has one method. That method is what you do when you are done in the EditPizzaViewController
, and want to go back to the OrderPizzaViewController
. It has a few parameters, things you want to pass back to the calling view controller. So it might look like this:
protocol PizzaEditDelegate{ func editPizzaDidFinish(pizza:Pizza,controller:EditPizzaViewController) }
I passed back a pizza with changed properties, and I sent back my View Controller. It’s often good to send back the controller to the delegate. We most often use it for dismissing the controller, but sometimes it may have a property we need.
In our new view controller EditPizzaViewController
, we make an instance of this protocol.
delegate:PizzaEditDelegate? = nil
Since we have a property of type PizzaEditDelegate
, we can use the methods of the PizzaEditDelegate
type, In our example, that is our method editPizzaDidFinish
. We can stick that method call in a action for a Done button:
@IBAction func doneButtonPressed(sender:UIButton!){ delegate!.editPizzaDidFinish(pizza, controller: self) }
Since protocols are skeletons, it means any other class can adopt them. A class makes the protocol methods part of its own class with a stipulation. As soon as a protocol gets adopted, you need to flesh out the skeleton. The developer has to code the required methods in the adopted class’ code. We adopt a protocol by placing it after the name of the class and superclass,
class OrderPizzaViewController:UIViewController,PizzaEditDelegate
As soon as you do that, you will get a compiler error since the protocol’s method does not exist in the class. In the code for the adopting class, in our example OrderPizzaViewController
, we would implement the method
func editPizzaDidFinish(pizza:Pizza, controller:EditPizzaViewController){ let orderNumber = controller.orderNumber pizzaOrder.changePizzaOrder(orderNumber,newPizza: pizza) controller.navigationController!.popViewControllerAnimated(true) }
We get the data back, place it where it belongs, and dismiss the newer controller taking the model and view with it using the controller
parameter. While it i s good documentation to place all your passed values as parameters, inthis example, orderNumber comes from its property of controller
. The pizza
from the new view controller is now a parameter, something we can work with in our model self.pizza
. I change our model via the original view controller calling a method of the model changePizzaOrder()
.
One more step. While back in the destination controller EditPizzaViewController
I said the delegate was an instance of the protocol, I didn’t say where the delegate was. In prepareForSegue
I add one more line vc.delegate = self
saying the protocol is your controller
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!){ if segue.identifier == "editOrder" { var vc = segue.destinationViewController as EditPizzaViewController let orderNumber = Int(orderNumberText.text!) vc.pizza = pizzaOrder.getPizzaOrder(orderNumber) vc.orderNumber = orderNumber vc.delegate = self } }
When we tap Done, the method runs, it knows it is located in the calling controller, and calls it there where we added to the original class.The data is a parameter so the program can easily transfer into the controller and to the model. Delegates and protocols are bit sneaky but it works, and is one of the most important techniques when working with view controllers.
The original question asked why didn’t Swift make this simpler. As I hope I’ve shown here with a Swift context, it doesn’t matter what object oriented language you use. Delegates are part of the MVC pattern, which is a good programming practice.
Pingback: Book Review: Sams Teach Yourself iOS 7 Application Development in 24 Hours | Making App Pie
Just stumbled upon your blog while looking for information about Swift and want to add that going reactive is a nice alternative you might consider. It simplifies things a lot (although it requires a mind shift).
See
http://peak.telecommunity.com/DevCenter/Trellis#model-view-controller-and-the-observer-pattern
.NET: http://msdn.microsoft.com/en-us/data/gg577609
Java: https://github.com/Netflix/RxJava
JavaScript: https://baconjs.github.io/
Cocoa: https://github.com/ReactiveCocoa/ReactiveCocoa
Your tutorials are very well written and I think your readers might like a tutorial on how to use this on iOS (with or without Swift).
Thanks !! You read my mind. That is actually on the schedule for sometime soon. I sorta fudged how the view communicates to the controller in this post, though a lot of it is notifications, plus a few features that look like notifications in Swift.
Good explanation!
Thank you!!!
Pingback: Swift Swift: Using Xibs and Delegates in Modal Views | Making App Pie
Pingback: Swift Swift: Programmatic Navigation View Controllers in Swift | Making App Pie
Great Tutorial but i’m trying it out and the code does not work:
I created this class “Pizza.swift”:
———————-Pizza.swift—————-
import Foundation
class Pizza{
var pizzaTopping:String
var pizzaCrust:String
var pizzaSize:Double
var pizzaRadius:Double {
get{
return pizzaSize / 2.0 }
}
let pi = 3.1415926
let crust = 0.1
init(topping:String,crust:String,size:Double){
pizzaTopping = topping
pizzaCrust = crust
pizzaSize = size
}
func howMuchCheese(height:Double) -> Double{
return (pizzaRadius * pi * (1.0 – crust )) * height
}
}
class PizzaOrder{
private var pizzaArray:[Pizza] = []
private var tableNumber:[Int] = []
func addOrder(pizza:Pizza, table:Int){
pizzaArray.append(pizza)
tableNumber.append(table)
}
func getOrder(index:Int) -> (Pizza,Int){
return (pizzaArray[index],tableNumber[index])
}
func getPizzaOrder(index:Int) -> Pizza{
return pizzaArray[index]
}
}
———————-PizzaController.swift——————-
private var pizzaSize:Double = 0.0
func setPizzaSize(size:Double){
pizzaSize = size
}
func getPizzaSize() -> Double{
return pizzaSize
}
———————PizzaView.swift———-
Can you please provide this code?
Thanks
SwCon
What doesn’t work with it?
Very nice, thank you.
Nicely explained :), It cleared my doubts regarding delegates to a great extent. Thanks a lot. Had been searching a lot about it but couldn’t find something so apt and precise. Thanks and please keep posting about other topics too.
You are very welcome– thank you for the compleny. Right now it sems Autolayout and class sizes are my thing, but I find that is another area where a good explanation is necessary.
Pingback: MVC, Enums, and Optionals | Coding Lawyer
Thanks for the link!
“PizzaOrderViewController” and “PizzaDemoViewController” should be “OrderPizzaViewController”, you use 3 different names to indicate one view controller.
“PizzaOrderViewController” and “PizzaDemoViewController” should be “OrderPizzaViewController”, you use 3 different names to indicate one view controller.
I’ll fix that. Thanks.
Very nice explanation! Thanks!
you are welcome.
Pingback: Swift WatchKit: Introducing Navigation to the Apple Watch(Part 3: Using Delegates and Contexts) | Making App Pie
Pingback: The Swift Swift Tutorial: Using Segues and Delegates in Navigation Controllers (Part 1 — The Template) | Making App Pie
Excellent explanation!! Thanks
Pingback: A Swift Tutorial for Working with Classes Part 3: Abstract classes. | Making App Pie
Pingback: Swift WatchKit: Selecting, Deleting and Adding Rows in an Apple Watch Table | Making App Pie
I’m begginer with programming and I tried to figured out what’s the big deal with delegates. And after your quide my mind blowed up and now everything is so clear! Thanks!
you are welcome
Pingback: The Swift Swift Tutorial: How to Use UITableView in Swift | Making App Pie
Pingback: How MakeAppPie.com is Updating to Swift 2.0 | Making App Pie
Excellent explaination…
Glad you liked it.
why we use segue to pass data? it can be done without using segue also. is so what is main use of using segue?
Segues keep track of where we are and a lot of background navigation information– particularly in navigation controllers. Segues are also linked to Interface builder, so it makes calling new scenes from the storyboard much easier. It is true that for modal controllers you don’t need a segue, but you should very rarely have more than one modal controller open for any length of time. you will, on the other hand have a lot of navigation controllers open. To keep those all straight, segues perform much of the work maintaining the navigation controller stack. Thirdly, overriding prepareforsegue is good communication and documentation of you code.
This really helpful for me, tks makeappie so much!!!
thanks so much i was trying to understand how delegates work and you explained it very well before coming here i check at least 9 websites
You are very welcome. It took me months to understand delegates, and the websites out there were little help to me too. I thought this and the other articles I wrote on this would help people.
Thanks for the tutorial! When I press the ‘save’ button, however, it does not go back to the original view controller. I have read all the comments and my view is embedded in the navigation controller, in prepareForSegue i have vc.delegate = self, and all other troubleshooting methods don’t seem to help.
–my question is, what changes the delegate? We set it to nil and then nothing (maybe saveColor or colorSelectionButton should?) seems to change it to anything else, so it doesn’t fall in the if statement
if (delegate != nil) {…}
I’d suggest you read this post very carefully, since it will answer your question me throughly than I can in a comment. Delegation is one of the most difficult ideas to wrap your head around, so read this post several times. The short answer to your question is the statement in a segue
vc.delegate = self
changes the delegate. I’ll quickly go through the process that a delegate works though if it helps:didFinish(data)
vc
in the current controller in theprepare for segue:
vc.delegate
to the current view controller before loading the destinationdelegate.didFinish(data)
.delegate.didFinish(data)
refers back to your initial few controller’s implemented delegate method and runs the code there. Since this code is happening in the initial view controller and not the destination view controller, you can set values from the parameterdata
to a variable in your initial controller.controller
, in the delegate method, the delegate method dismisses the controller. If not, the code in the destination controller’s done action dismisses the controllerPingback: Using Segues and Delegates for Navigation Controllers in Swift 3.0 | Making App Pie
I HAVE MENTIONED IN BLOCK LETTERS BELOW THE LINE I NEED EXPLANATION … locationmanager.delegate = self IN THAT WHAT DOES “.delegate” and assigning self mean? and what are we doing there?
//
// ViewController.swift
// SpeedMap
//
// Created by Elton Menezes on 7/7/16.
// Copyright © 2016 Menezes Elton. All rights reserved.
//
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController,CLLocationManagerDelegate,MKMapViewDelegate {
@IBOutlet weak var myMap: MKMapView!
@IBOutlet weak var speedLabel: UILabel!
@IBOutlet weak var locationLabel: UILabel!
var locationmanager = CLLocationManager() //referring to the class here
var overlay = MKCircle() //referring to the class here
var latDelta: CLLocationDegrees = 0.5
var longDelta: CLLocationDegrees = 0.5
override func viewDidLoad() {
super.viewDidLoad()
locationmanager.delegate = self (PLEASE EXPLAIN TO ME THIS LINE)
locationmanager.desiredAccuracy = kCLLocationAccuracyBest//specifies the location accuracy
locationmanager.requestWhenInUseAuthorization()//Asking the user for authorization
locationmanager.startUpdatingLocation()
speedLabel.text = “0 km/hr”
locationLabel.text = “”
myMap.delegate = self
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let myLocation: CLLocation = locations[0] as CLLocation
let latitide: CLLocationDegrees = myLocation.coordinate.latitude
let longitude: CLLocationDegrees = myLocation.coordinate.longitude
let span: MKCoordinateSpan = MKCoordinateSpanMake(latDelta, longDelta)//Structure that defines area spanned by the map region so we need the zoom variables of latDelta and longDelta
let location: CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitide, longitude)
let region: MKCoordinateRegion = MKCoordinateRegionMake(location, span)//defines which portion of the map to display
myMap.setRegion(region, animated: true)//Keeps changing the region to be displayed
let speed = Int(max(myLocation.speed, 0) * 3.6)
speedLabel.text = “\(speed) km/hr”
CLGeocoder().reverseGeocodeLocation(myLocation) { (placemarks:[CLPlacemark]?, error: NSError?) -> Void in
if error == nil {
let placemark = placemarks?.first//We would always get the first object which would give the updated location
let subthoroughfare = placemark?.subThoroughfare != nil ? placemark?.subThoroughfare : “”
let throughfare = placemark?.thoroughfare != nil ? placemark?.thoroughfare : “”
let country = placemark?.country != nil ? placemark?.country : “”
let location = “\(subthoroughfare!) \(throughfare) \(country)”
self.locationLabel.text = location
}
}
}
@IBAction func zoomIn(sender: AnyObject) {
}
@IBOutlet weak var zoomOut: UIBarButtonItem!
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
This was an excellent question. The very short answer is you want your delegate methods in the same class. However, that doesn’t do much justice to the answer, so I wrote a whole post on the subject you can find here: https://makeapppie.com/2016/07/10/why-does-apple-need-delegates/
Pingback: Why does Apple Need Delegates? | Making App Pie
Pingback: Programmatic Navigation View Controllers in Swift 3.0 | Making App Pie
Shouldn’t this: “The controller can send messages to the view and the controller. The view and controller may do an internal action to the message sent as a method call or it may return a value to the controller.”
rather read as that: “The controller can send messages to the view and the model. The view and model may do an internal action to the message sent as a method call or they may return a value to the controller.”?
Yes it should. Thanks!!! Fixed it, although I wrote a newer one with correct Swift 3.0 Syntax and a lot better illustrations here.
Great post, Steven! Thank you for taking the time to write it. I’m still trying to wrap my brain around this and I have a question for you. In ‘The Problem Direction’ you said, “The new view controller does not know anything about the class that called it. We look stuck.” and I understand that, but in ‘prepareForSegue’ we’re getting a reference to to view controller we’re going to segue to so therefore it knows something about the class it’s calling. Why doesn’t this knowledge violate the principles of MVC and encapsulation?
First, just so you know, I just updated this with a new version:
And now to your question. Let’s look at the line of importance in
prepare(forSegue:)
This gets a pointer to the destination controller
vc
then sets a property ofvc
namedpizza
, which the destination controller in turn sets its model of classPizza
.A controller can set or get properties from the classes it knows, i.e. that were instantiated within its code. A different way of describing MVC is that the job of the controller: Being in control of the model, view, and any other classes it calls, which usually is the controller of another MVC. It gets and sets the values of one class to another: Controllers are the transfer station with logic. It would break MVC to have this controller directly access another controller’s model, but it can instantiate another view controller and set the properties of the controller. That is what goes on in this
prepare(forSegue:)
.You’ll find an even more barefaced example in this code for presenting a modal:
As long as you change properties in
vc
only you are not breaking MVC.pizza
is a property ofvc
, so you can move the pizza ordered from the current MVC to the destination MVC.The way this is all set up its actually difficult to break MVC. A shared model would break MVC, where two controllers control a single model. Two controllers talking to each other doesn’t break MVC.I hope the individual who one is looking to build a career in IOS Android this will be helpful.