When I was in university, I had to take a course called “Foundations of computer science”. Since this course was placed in the second trimester of the first year, it was an immediate roadblock for many people. Unlike others, I found the course to be very easy and passed the exam quickly with the maximum grade, with almost no studying. I am not saying this to brag, but I think there is an important lesson there.

The professor teaching that course was later my thesis supervisor. One day she was talking about the people could not pass the exam for her course. I will always remember her words: “People pass exams in the first trimester just memorizing things and then get stuck on my exam because I ask them to think”. She was spot on and that is why I passed the exam so easily. Unlike others that looked at it like some boring theoretical stuff, I took the time during the classes to understand what she was explaining, instead of just copying mindlessy what she wrote on the blackboard. Then, I just had to review the material for the exam, while the people that failed the exam just tried to memorize everthing at the end, without understanding.

I think that iOS development is like that exam. You can try to copy and memorize every tutorial you find, but if you do not take the time to understand how the platform works, you will fail the exam: making an app on your own. This is what allowed me to become an iOS developer. I always try to understand things first. Honestly I never followed a tutorial in my whole life. I definitely read a lot of material and blog posts from other developers about many topics and they usually provide very helpful exercises, but I never saw the value in following a tutorial that just shows me the how without first making me understand the concepts.

I have talked extensively about the how things work in iOS in my previous articles. Until now I have withheld practical examples on purpose. I do not want them to be just another tutorial where you just write a bunch of code and have not progressed one step at the end of it. But now that we have seen and understood how iOS apps work, it is time to see it in practice. That is why I call this an example and not a tutorial. Understanding comes first and then the example reinforces the concepts and clears doubts. There will be links to my other relevant articles through the whole example. If you have not done it yet, I advise you get familiar with them first. Otherwise this example will be just another tutorial that does not move you forward.

In this example we will build a simple app that shows your favorite quote on the screen. This quote can also be edited and saved so that the next time you open the app, it is still there. Although simple, it will cover many core concepts of iOS development:

the MVC pattern and model controllers

view controllers and their lifecycle

the view hierarchy

storyboards

basics of Auto Layout

outlets and the target-action pattern

view controller containers

communication between view controllers and delegation

As you can see, even the simplest app, if done properly, covers many of the basic concepts you need to know in iOS development.

So let’s get started. You can find the whole Xcode project for this example on GitHub.

How the model view controller pattern works in iOS

I have already talked about the MVC pattern and view controllers. Let’s have a recap of how it works in iOS. The MVC pattern divides classes in three different layers

The model encapsulated the data and the logic of the app.

Views are what you see on the screen. They present the data to the user and allow interaction.

The controller layer is responsible of mediating between the other two. Controllers connect the views to the data and are also responsible for setup, coordinating tasks and interpreting user input in the app specific way.

Of the three layers, the controller layer is the only one that knows about the other two. Views know nothing about controllers or the model and the model knows nothing about views or controllers. The boundaries between the layers of the MVC pattern are usually not sharp and indeed we find different kind of controllers there.

At the boundary between model and controllers we find model controllers. They manipulate the model and encapsulate the app logic that does not belong to the model. Examples are saving on disk or communicating over the network. Model controllers usually only know about the model or other model controllers.

At the boundary between views and controllers we have view controller. View controllers are controllers that know about the user interface in a more detailed way and know how to interpret user action according to the app logic. View controllers play a central role in iOS since they usually represent one screen each. Each view controller has an associated view that usually (but not always) fills the screen and on which the user interface for that screen is assembled.

Indeed in an usual iOS app you do not usually find pure controllers (according to these definitions), so you usually have four layers instead of three:

model

model controllers

view controllers

views

I guess that MVMCVC pattern does not sound as good as MVC.

So, let’s start creating our sample app and visualizing where these different layers are. Open Xcode and create a new project with the “Single view application” template. This template is the basis of apps with only one screen, but I always start from this template anyway and add screens when needed. We are going to make this app for the iPhone. Let’s have a look at the files we find in the project.

The two files we are interested in here are the ViewController.swift and Main.storyboard.

This is a single screen app, which means it has only one view controller at the moment. ViewController is the class that represents it. All view controllers in iOS must descend from the UIViewController.

In the Main.storyboard file we find the layout of all the screens our app has. Since our app has only one screen for now, the storyboard show only one. Scene is the name a screen of our app has in a storyboard, and thus represents one view controller. I will use these terms interchangeably in this example. This scene is empty for now.

There are two important things to know here at the moment. First, how do we know that this is the starting screen of our app? Now it is obvious, because there is only one screen, so it must be it. But a storyboard usually contains many scenes so we need to know where to start from. Look at the scene: it has an arrow on its left, pointing at it. This arrow shows that this is the initial view controller of the storyboard (and hence of our app, since there is only one storyboard). You can actually drag that arrow to another view controller when you want you app to start from another place. You can also change check the “Is Initial View Controller” checkbox in the Attributes inspector.

This scene in the storyboard represents the view attached to the view controller. The view controller itself is actually represented by the little yellow circle at the top of the scene. The important thing to know here is how this little circle in the storyboard is connected to the ViewController class in the swift file. You can specify the class of any object in a storyboard through the Identity inspector. For this view controller it is already set to the correct class because it came from the template, but for all other view controllers we will add we will have to do it ourselves.

So here we have already two of the layers that we have seen. The view layer, represented by the scene in the storyboard, and the view controller layer, represented by the ViewController class. Let’s build the other parts of the MVC pattern.

Laying out the model of the app

Model objects represent the data of our app. We want the app to display a quote on the screen, so this is the only data we need for this example. A quote has text and an author, which are both strings. We can represent this with a class.

1 2 3 4 5 6 7 8 9 class Quote : NSObject { let text : String let author : String init ( text : String , author : String ) { self . text = text self . author = author } }

Pretty straightforward (why didn’t I use a struct here instead of a class? We will see that in a minute). This is our data structure, but we need some actual data to display on the screen. I have chosen this quote from Albert Einstein:

Two things are infinite: the universe and human stupidity; and I am not sure about the universe.

To hold this data we have to create an instance of our Quote class. But where should we do this? Many would do it in the view controller, but hold on for a second. A view controller should only take care of moving data from the model to the user interface. It should not create the data itself. The model, on the other hand, should not be concerned of how some specific data is actually retrieved, but should only of representing it.

This data does not pop out of nowhere and it is usually retrieved from a local storage or the network. Again, this is neither a concern for the model nor it is for a view controller, so we need a specific object to take care of this. This object is a model controller. You can read more about this in my article on how to structure the code of iOS apps. Our model controller creates a new instance of the Quote class to be used by the view controller.

1 2 3 class ModelController { let quote = Quote ( text : "Two things are infinite: the universe and human stupidity; and I'm not sure about the universe." , author : "Albert Einstein" ) }

This is a very simple example of a model controller that does not really give an idea about the separation between model and model controllers. After all, it contains only one line of code. To better show why it needs to be separated, let’s expand it. Our app will need to give the user the option of editing or replacing this quote with another quote he likes more. We want his changes to be permanent, so the next time he opens the app he will still see his new quote. Let’s add some code to the model controller to save a quote on disk.

To achieve this we are going to use the the keyed archiving functionality of iOS. The method itself is not important, so I am not going to go into the details of this. There are many ways an object could be saved on disk: keyed archiving, a property list, using an NSFileManager to create a file, Core Data, etc. I have chosen keyed archiving because it is the simplest one of all, but a more complex app could use another method.

To be archived a class needs to conform to the NSCoding protocol. This protocol requires a class to provide two methods:

an initializer to decode itself from encoded data

a method to encode itself, so that it can be saved on disk or transmitted through the network

NSCoding is a class protocol, and this is why I used a class for the quote and not a structure. Hopefully in the future this will be extended to structures too.

Encoding and decoding of an instance is pretty straightforward. What you need to do is just call the appropriate methods of the NSCoder object passed as parameter and give each property a key.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Quote : NSObject , NSCoding { let text : String let author : String init ( text : String , author : String ) { self . text = text self . author = author } required init ( coder aDecoder : NSCoder ) { text = aDecoder . decodeObjectForKey ( Keys . Text . rawValue ) as ! String author = aDecoder . decodeObjectForKey ( Keys . Author . rawValue ) as ! String } func encodeWithCoder ( aCoder : NSCoder ) { aCoder . encodeObject ( text , forKey : Keys . Text . rawValue ) aCoder . encodeObject ( author , forKey : Keys . Author . rawValue ) } enum Keys : String { case Text = "text" case Author = "author" } }

We can now change the model controller to save a quote and to retrieve it when needed. If there is no quote saved on disk yet, our model controller simply returns the default Einstein quote. We can do this in the getter and setter of the quote property.

1 2 3 4 5 6 7 8 9 10 11 12 class ModelController { let quoteFilePath = NSSearchPathForDirectoriesInDomains ( . DocumentDirectory , . UserDomainMask , true ) . first ! + "/quote.txt" var quote : Quote { get { return NSKeyedUnarchiver . unarchiveObjectWithFile ( quoteFilePath ) as ? Quote ? ? Quote ( text : "Two things are infinite: the universe and human stupidity; and I'm not sure about the universe." , author : "Albert Einstein" ) } set { NSKeyedArchiver . archiveRootObject ( newValue , toFile : quoteFilePath ) } } }

Hopefully this will give you a better picture of how the model and the model controllers of an app are separated in concerns. In general you can see that the Quote model class is concerned about representing the data and converting itself to and from different formats. More complex model classes might be concerned about their interaction with other model classes as well. But they are not concerned about specific data, nor they are about other concepts like saving on disk or transmitting over the network. That logic is usually contained inside model controllers.

Laying out the user interface using Auto Layout

Now that we have created the model of the app, we will go to the opposite side of the MVC pattern, the view layer, where we will create the user interface for our app. As we have seen above, the storyboard contains already a scene for the view controller in which we can assemble the user interface for the first screen of the app. We will use Auto Layout for this but this is not intended to be a complete guide to Auto Layout, since it is a quite powerful technology that can be quite complex. If you need an in-depth guide for Auto Layout, you can find one here, The point of this example is to understand how the different core concepts of iOS interact to make apps behave like we want.

First of all, let’s look at the scene in the storyboard. Why does it have a square shape? We are making an app for the iPhone, so should it not be rectangular? The reason it has this shape is that through the years iPhones have evolved to have different screen sizes and ratios. To handle this, Apple has introduced first Auto Layout, to make it easier to create dynamic interfaces that adapt to any screen size, and then size classes, to make it easier to make interfaces that change their layout depending on the screen ratio and direction. By “adapt”, in the case of Auto Layout, I mean that the interface element are moved and resized proportionally to fill the screen, while by “change”, in the case of size classes, I mean that they can completely rearrange their content and show different information based on the size and direction of the screen. The square shape of this scene in the storyboard indicates the scene works for any size class both in height and in width. This is the standard and easier case, where we want our interface to adapt to the screen but we do not want to completely change the layout.

We are not going to see size classes here, since most interfaces are designed to be the same on any screen. But using Auto Layout is a must for any interface you make nowadays. Without Auto Layout, the normal way of placing views is by giving them an absolute position and size through coordinates. This is static and does not change with the screen, so the app looks bad on some devices. With Auto Layout instead we express constraints that describe how the interface should look like on any screen. The layout engine will take those constraints and calculates the correct positions and sizes for each interface element based on the actual size of the screen they will be displayed on.

Let’s see an example that will make this much easier to understand. We want to display our quote on the screen, so we need to display both the text and the author. We want to make this interface a bit more fancy, so we will place big quotation marks at the beginning and the end of the quote text. To display static text on the screen in iOS we use a specific type of view called label. To know which standard views are available for you to use to make your user interfaces you can check Apple’s iOS views catalog.

Let’s start by placing the open quotation mark and making its font bigger to make it more visible.

We want this label to always be in the upper left corner of the screen. We have to express this description through Auto Layout constraints. This means the label needs to always have the same distance from the top and the left edges of the view. We can add these constraints from the Auto Layout Pin panel in the bottom right corner of the storyboard. Here we set the distance of the label from the top and from the left margin of the view. If you placed your label in the desired position manually when you dragged it, this panel will already contain the desired values. In this case they are both 0 because I placed the label exactly on the margins. Interface builder shows you alignment guides when you position views.

These two constraint are enough for this label. In general, you need to express four properties for each view when setting constraints:

vertical position

horizontal position

height

width

This does not mean that you have to specify four constraint for every element. Each property might be the result of one or more constraint, depending on ow complicated is your layout. It can also come from the content of the view, thus requiring no constraints. This is called intrinsic size, and Auto layout uses it together with constraints to calculate the final layout. The two constraints we placed specify the vertical and horizontal position of the label, while its height and width are automatically calculated based on the content of the label.

Now we need to place the label for the quote. To have an idea of how big a label with some text is going to be, we are going to place some Lorem ipsum text inside of it in Interface Builder.

I have placed the label where I want it to be, but it definitely does not look like the final result we want. Let’s describe with words what we want from this label. First of all, it needs to expand down based on its content, so it can always display the full quote. It also needs to always stay between the quotation marks, so it should not expand horizontally too much, or it will go out of the screen on the right. That is a quite complicated description, so it will need some layout constraint to achieve this.

Let’s start with the easy part. To make a label expand indefinitely based on its content we can set its number of lines to 0. Then we can place constraints in the same way we did for the other label to keep it always at the same distance from the top edge of the screen and from the quotation mark label on its left. Let’s also place the closed quotation mark and resize the quote label manually so that it looks approximately like the result we want to achieve.

Notice that I placed the left constraint to be from the quotation mark label and not from the left margin. This is because if we change the size of the font of the quotation marks, we want the quote label to adapt and fill the space. If we set the distance to be from the left edge, the quote text label would always stay in the same place.

Let’s now translate our description into constraints. We want the quote text to always stay in between the quotation marks, so it needs a constraint that keeps it always at the same distance from the right quotation mark. The quotation mark itself needs to always be at the same distance from the right margin, like the other one.

You will notice that the labels edges and the constraints are shown in yellow. This is how interface builder displays ambiguous constraints, which are the constraints which Auto Layout does not know to fulfil. This is because they do not express enough information to have a unique solution, so there are many different ways to satisfy them (if they are displayed in red instead, it means that some constraints are contradicting others, so they cannot all be satisfied at the same time).

Why are they yellow in this case? It is because of two reasons. First, we did not express the vertical constraints yet. Remember we have to always express the four properties listed above. We want the quote text to expand vertically, but still stay be between the quotation marks. This means that the right quotation mark needs to move up or down to adapt to the changing height of the quote label. We can do this by placing a constraint to align the bottom of the quotation mark to the bottom of the quote text label.

Even adding the vertical constraints, the yellow color does still not go away. This is because there is still a problem. This one is less evident and more subtle. It seems that we expressed all the constraints we needed, but the layout engine still cannot figure out how to place our labels. This is because in our mind we have some constraints that we think we expressed in Interface Builder, but we did not. The problem is this: the label needs to keep a fixed distance from the right quotation mark. But the label also expands horizontally to make room for its. We have a situation where the quote text label pushes from the left and the quotation mark label pushes from the right to maintain its size and be able to show the full quotation mark. Which one of the two labels should be stronger? To us it is clear that the quotation mark should be stronger and not get smaller, but to the layout engine both labels have the same importance. Thus, it cannot decide which one should give way to the other.

We can change this by making one label push stronger than the other. This is done by changing the content compression resistance priority of one of them. Now they both have a priority of 750. We can lower the priority of the text label to 749, to indicate that this one should be weaker and adapt to the size of the quotation mark label.

The constraints are now unambiguous and so they turned blue. We still have to add a small label for the author of the quote to our interface. This label should always keep the same distance from the bottom of the text label, so it can move down as the other label expands. It should also keep its distance from the right margin, to be always right aligned, regardless of the length of the author name. These are straightforward constraint, that we can place in the same way we did for all the others.

Our interface is now complete, but how do we test that it really works on any device size? One way is to run the app in the simulator for each type of iPhone, but this is tedious and takes time. It would be even more so if this screen would not be the first in the app but would only be reachable after a long series of other screens. Luckily Interface Builder has a preview in the assistant editor that allows us to see the result of the constraints we expressed.

Here you can add different iPhone screens to check how your interface behaves on all of them without having to run the app in the simulator.

To recap, we have seen how to assemble the user interface for a view controller in Interface Builder using Auto Layout. We have just scratched the surface of what Auto Layout can do, but this was not intended to be a complete guide. The important thing to understand here is how we use constraints to express the position and the size of the different elements of our interface. With a few of the constraint you can express in Interface Builder you can go a long way and make interfaces that adapt to any device screen.

Connecting the user interface to code through outlets

Now that we have both a model for our app and an interface, it is time to connect the two through the view controller. As we have seen both the model and the views should not know anything about the other layers. View controllers are responsible to make this connection between the two layers of the MVC pattern.

The first task view controllers need to carry to hold this responsibility is to make the data flow from the model to the user interface so that the user can see it. To be able to do this, a view controller needs references to the various views in the interface. At the moment our view controller has no such references. The only reference it holds is to its main view. The rest of the user interface is built in Interface Builder and contained in the storyboard, so we need a way to connect it to the view controller code.

This is done in iOS through what are called outlets. Outlets are just properties on a class, like other properties, but they are marked by the special keyword @IBOutlet that makes them visible to Interface Builder. In this way they can be connected to views in the storyboard and when the user interface is created, the system fills the outlet properties with references to the views, making it possible to access them from code. If you want to know more about this process, check the initial phases of the lifecycle of a view controller.

It is possible to write these outlets by hand and then connect them in Interface Builder, but there is a faster way. You can use the Assistant editor in Interface Builder to bring up the class for the view controller. Then you can control drag from a view in the interface to the code of the view controller. The outlet will be created with the correct declaration and connected to the view automatically.

We need an outlet for the text label and one for the author label. Having these connections we can finally fill the user interface with the data coming from the model. We do this in the viewDidLoad() method of the view controller. This is the correct place because when this method is called, we are guaranteed that all the outlets have been connected to their respective view. Before this method is called, outlets are not filled yet. Again, reference the lifecycle of a view controller to know more about this.

First of all, we need an instance of the model controller from which to get the quote. Then we can pass the quote text and author to the outlets.

1 2 3 4 5 6 7 8 9 10 11 12 13 class ViewController : UIViewController { @ IBOutlet weak var quoteTextLabel : UILabel ! @ IBOutlet weak var quoteAuthorLabel : UILabel ! let modelController = ModelController ( ) override func viewDidLoad ( ) { super . viewDidLoad ( ) let quote = modelController . quote quoteTextLabel . text = quote . text quoteAuthorLabel . text = quote . author } }

We have finally connected the layers of the MVC pattern to each other. Now the data flows from the model to the views through the view controller. Some tutorials show you see the view controller creating and manipulating the data to display in the user interface, but this is not what is normally done in a real app. Instead, the concerns are separated in the four layers of the MVC pattern we have seen above. In this way, the model objects and the model controllers are reusable across the app and can even be used in other apps. If you do everything in a view controller instead, that code will be hard to reuse. Your only way will be to copy and paste it. The usefulness of this approach is going to be even more evident in the next part of this example, where we will be passing this data between different view controllers.

Using a navigation controller to move between screens

We have now an app that shows our favorite quote on the screen. What we miss is a way to change this quote whenever we find a new one we like more.

To do this we need a new screen with text fields to edit the quote. A new screen means a new view controller. We can start by adding a new empty view controller scene to our storyboard, next to the one we already have.

Now, how will we move from the first screen to the second? The way to move between view controllers in iOS is to use a container view controller. A container is a special kind of view controller that manages a list of view controllers instead of managing a single screen. For this example we are going to use the most common container used on the iPhone, the navigation controller. A navigation controller enables the common right to left navigation you often see in iPhone apps, where the next view controller slides in from the right of the screen and you have a back button in the top left corner to go back to the previous view controller. I spoke more in detail about different container view controllers here.

Let’s start adding a navigation controller to the storyboard. We can place it to the left of the current initial view controller (a navigation controller comes in Interface Builder with a table view controller attached to it. We do not need that, so we can delete it). Since the navigation controller is the container that needs to manage the other two view controllers, it needs to be the initial view controller of the app. This is because, hierarchically speaking, a container needs to come on screen before the view controllers it contains, to be able to bring them on the screen when needed. You can read more about structuring an app with containers here.

After setting the navigation view controller as the initial view controller for our storyboard, we need to tell it that the view controller containing the quote is the first view controller it will have display. We can do this by control dragging from the navigation view controller to the quote view controller and selecting the “root view controller” relationship segue.

You will notice that our quote view controller gets a navigation bar at the top after making the connection to the navigation controller. Many make the mistake to think that the navigation bar is owned by the view controller and think that every view controller has its own navigation bar. This is not how it works: a navigation bar belongs to a navigation controller. Each view controller gets instead its own navigation item, which represent a state of the navigation bar and can include a title and other controls like buttons. This navigation item gets inserted in the navigation bar when the corresponding view controller is on screen.

The navigation bar in the view controller scene seems to be covering our labels, but it is just because their frames have not been updated to consider the navigation bar yet. Interface Builder takes care of this automatically. We can update the frames of the views in a view controller using the “Resolve Auto Layout Issues” panel in the lower right corner.

We now need to connect the two view controllers to each other. First of all, we need to place an edit button in the navigation bar for the quote view controller. We can drag a “Bar Button Item” from the object library to the right corner of the navigation bar and set its identifier to “edit” in the Attributes inspector. We can then control drag from this button to the next view controller to connect a “show” segue to it. This segue will be triggered when the user taps on the edit button. The next view controller will also get a navigation bar in the storyboard when we make this connection.

If you run the app, this will work already. You can navigate to the next view controller by tapping the edit button and in the next screen there will be a back button to go back to the previous screen. This is all handled by the navigation controller.

The new view controller is still blank, so let’s fill its interface. We need a text view for the quote text and a text field for the author. We can add little labels on top of these to indicate what they are for. Setting the layout constraints works the same way as we saw for the first view controller, so I am not going to go over the process again. The only difference from labels is that the text view needs to have a constraint that fixes its height. Text views do not expand when the text changes like labels do. Do not make it too long or it will go under the keyboard when it comes on screen (we could move the interface up to adjust to the keyboard, but I do not want to complicate this example too much).

We also need a button on the top right corner, in the navigation bar, to save the new quote. Tapping on the back button will be our way to cancel the editing (wether this is a good design decision can be debated, but this is just an example app). We will not be able to directly add the save button to the navigation bar, because this second view controller did not get a navigation item automatically like the first one did when we made the connection. So we first need to add a navigation item to the navigation bar for this view controller and then add the button to it. Remember that the navigation bar belongs to the navigation controller, but each view controller needs a navigation item for itself.

This is the final result:

This is the user interface of the view controller, but we still do not have any class for it. We need to create a new file for this new class (in iOS usually view controller classes are placed in their own Swift files). We can call this class EditViewController. I also renamed the ViewController class to QuoteViewController, which is a better name for it. Remember to set the class for the edit view controller in the identity inspector in the storyboard:

The edit view controller needs outlets to the text view and the text field like we did for the quote view controller. But it also needs to react to the tap on the save button. A view controller role is to connect the view layer to the model layer, so it is normal for it to know about views through outlets. Interface elements like buttons are instead meant to be generic and reusable, so the save button does not know anything about the view controller it is contained in.

Still, this button needs to communicate with the view controller to notify it of user taps. In iOS this is done through another common design pattern: the target-action pattern. In this pattern, a generic interface element like a button only needs to know a target to notify and an action to call on it when the event occurs. The action is a method that the target implements. In this way a view can still communicate with a view controller without having to know everything about it. The target and the action for a control can be set and even changed at runtime. The target can be any kind of object and the action any method that has no parameter or a single parameter through which a reference to the caller is passed. In this way a control remains generic enough to be attached to any kind of object.

We can connect actions from a button to a view controller in interface builder, in the same way we connected outlets: control dragging from the button to the view controller code. What will happen in this case though is the opposite of what happened for outlets: the button is the object that has a reference to the view controller and instead of a property in the view controller, a method will be created. The method is marked by the special keyword @IBAction, to make it visible to interface builder. Like the @IBOutlet keyword, this does nothing else. It is also possible to first type this method by hand and then connect it to the button, but control dragging is faster.

This is the code for the edit view controller.

1 2 3 4 5 6 7 8 class EditViewController : UIViewController { @ IBOutlet weak var textView : UITextView ! @ IBOutlet weak var textField : UITextField ! @ IBAction func save ( sender : AnyObject ) { } }

We now need to pass data between the two view controllers, so that we can edit and save the quote.

Communication between view controllers and delegation

We need to have the communication between the two view controllers go in both directions.

First we need to pass data forward to the edit view controller so it can display the current quote to be edited. At the moment the two view controllers do not know anything about each other and have no connections. The transition between the screens, both forward and backwards, is handled completely by the navigation controller, so for now the two view controllers have not needed to know about each other at all. But now they need to communicate and pass data to each other.

As I have explained in my article on how view controllers communicate, when two view controllers are connected by a segue inside of a storyboard, the only moment they have a connection is when the segue is triggered. At this moment the prepareForSegue(_:sender) method of the origin view controller gets called, and the origin view controller has a chance to get a reference to the destination view controller it can use to pass data forward.

To be able to receive data, the destination view controller needs to have a property where this data can be stored by a previous view controller. In our example, the EditViewController needs to get the quote to display.

1 var quote : Quote ?

This property is declared as an optional because it cannot be set at the time the view controller gets initialized (to know more, read my article on the lifecycle of a view controller). Through this property we can pass the quote between the view controllers, in the prepareForSegue(_:sender) of the first view controller:

1 2 3 4 5 override func prepareForSegue ( segue : UIStoryboardSegue , sender : AnyObject ? ) { if let editViewController = segue . destinationViewController as ? EditViewController { editViewController . quote = modelController . quote } }

In the viewDidLoad() method of EditViewController we can populate the user interface with the quote data.

1 2 3 4 5 override func viewDidLoad ( ) { super . viewDidLoad ( ) textView . text = quote ? . text textField . text = quote ? . author }

We now need to communicate back the data of the quote when it gets edited and the user taps on the save button. To do this, we need a delegate protocol.

Why don’t we just use a direct reference to the previous view controller? It would indeed be possible to add a new property to the EditViewController with type QuoteViewController. Though this reference the EditViewController would be able to communicate back to the previous view controller. This would indeed work, but it would create a problem. To make this work the EditViewController would need to know the type of the previous view controller. This is necessary to know which properties and methods to use. This is a very simple app and we have only two view controllers, so the problems that arise from this approach might not be evident. Let’s go over them.

The first problem would be that this two classes would become tightly coupled. The QuoteViewController knows already the interface of the EditViewController. If the EditViewController would have knowledge of the QuoteViewController too, the coupling between the classes would increase. So, when we would change the QuoteViewController code, we would have the potential to break the EditViewController.

The second problem is that In a more complex app, a view controller is likely to be reached through different paths. So the previous view controller could be of different types. If we used direct references, we would need different properties of different types, one for each view controller that could come before. This approach would require a lot more code. It would also again increase the coupling between this view controller and all the ones that could come before it.

The solution here is to use delegation. Delegation is another very common design pattern in iOS. In the delegate pattern, an object can communicate with a delegate object without knowing its type. All it needs to know is that the delegate conforms to a protocol. This limits the knowledge the delegating objects needs to have of the delegate, thus making it possible to set as a delegate any kind of object that conforms to such protocol, regardless of its type.

The delegate pattern is used to achieve many different things: passing data between objects, notifying other objects of events and even delegating certain decisions that the delegating object cannot take by itself. In this case, we are going to use it to enable backwards communication between view controllers.

First we need to define a protocol with a method to pass a new quote back. This protocol is then used as a type of a property that will contain the reference to the delegate. Through this reference we can pass the quote back in the save method of EditViewController.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 protocol EditViewControllerDelegate : class { func quoteWasSaved ( quote : Quote ) } class EditViewController : UIViewController { @ IBOutlet weak var textView : UITextView ! @ IBOutlet weak var textField : UITextField ! var quote : Quote ? weak var delegate : EditViewControllerDelegate ? override func viewDidLoad ( ) { super . viewDidLoad ( ) textView . text = quote ? . text textField . text = quote ? . author } @ IBAction func save ( sender : AnyObject ) { let newQuote = Quote ( text : textView . text , author : textField . text ! ) delegate ? . quoteWasSaved ( newQuote ) self . navigationController ? . popViewControllerAnimated ( true ) } }

Beware that in this example I called popViewControllerAnimated(_:) on the navigation controller for simplicity. Since we use storyboards, a more appropriate way to go back would be using an unwind segue.

I also did not check for empty fields. A more appropriate flow would show an error message to the user if the quote text or author were left empty.

We now have to enable the QuoteViewController to receive the new quote, by conforming to the protocol. In the protocol method, the QuoteViewController can save the new quote through its reference to the ModelController, in which we already wrote such logic. We also need to populate the delegate property of EditViewController from the QuoteViewController. We can do so again in the prepareForSegue(_:sender:) method, where data is passed forward. Do not forget to update the UI of QuoteViewController or the changes to the quote will not be visible.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class QuoteViewController : UIViewController , EditViewControllerDelegate { @ IBOutlet weak var quoteTextLabel : UILabel ! @ IBOutlet weak var quoteAuthorLabel : UILabel ! let modelController = ModelController ( ) override func viewDidLoad ( ) { super . viewDidLoad ( ) updateUIWithQuote ( modelController . quote ) } override func prepareForSegue ( segue : UIStoryboardSegue , sender : AnyObject ? ) { if let editViewController = segue . destinationViewController as ? EditViewController { editViewController . quote = modelController . quote editViewController . delegate = self } } func quoteWasSaved ( quote : Quote ) { modelController . quote = quote updateUIWithQuote ( quote ) } func updateUIWithQuote ( quote : Quote ) { quoteTextLabel . text = quote . text quoteAuthorLabel . text = quote . author } }

Our app is finally complete.