Book Image

Swift: Developing iOS Applications

By : Jon Hoffman, Andrew J Wagner, Giordano Scalzo
Book Image

Swift: Developing iOS Applications

By: Jon Hoffman, Andrew J Wagner, Giordano Scalzo

Overview of this book

The Swift––Developing iOS Applications course will take you on a journey to become an efficient iOS and OS X developer, with the latest trending topic in town. Right from the basics to the advanced level topics, this course would cover everything in detail. We’ll embark our journey by dividing the learning path into four modules. Each of these modules are a mini course in their own right; and as you complete each one, you’ll gain key skills and be ready for the material in the next module. The first module is like a step-by-step guide to programming in Swift 2. Each topic is separated into compressible sections that are full of practical examples and easy-to-understand explanations. Each section builds on the previous topics, so you can develop a proficient and comprehensive understanding of app development in Swift 2. By the end of this module, you’ll have a basic understanding of Swift 2 and its functionalities. The second module will be the an easy-to-follow guide filled with tutorials to show you how to build real-world apps. The difficulty and complexity level increases chapter by chapter. Each chapter is dedicated to build a new app, beginning from a basic and unstyled app through to a full 3D game. The last two chapters show you how to build a complete client-server e-commerce app right from scratch. By the end of these modules, you’ll be able to build well-designed apps, effectively use AutoLayout, develop videogames, and build server apps. The third and the last module of our course will take an example-based approach where each concept covered is supported by example code to not only give you a good understanding of the concept, but also to demonstrate how to properly implement it.
Table of Contents (6 chapters)
4
A. Biblography
5
Index

Until this point, we have been concentrating almost exclusively on learning Swift without learning much about the platforms that it was designed for. This is because learning a new platform is a completely different world from learning a language. Learning a programming language is like learning the basic grammar of a spoken language. The grammar between the spoken languages generally expresses similar concepts but the specific words of the languages are often more varied, even if they are sometimes recognizable. Learning a programming language is learning how to connect the specific vocabulary of your desired platform. This chapter will be about learning some of the vocabulary of the iOS framework.

We will do this by going through the process of starting to develop a simple camera app. Along the way, we will learn some of the most critical vocabularies to get started with any other kind of iOS app and many of the concepts will be transferable to OS X development. More specifically, we will cover:

Before we even open up Xcode, we should have a good sense of what we plan to develop. We want to know the basics of what kind of data we are going to need to represent and what the user interface is going to be like. We don't yet need pixel perfect designs for every screen, but we should have a good idea of the flow of the app and what features we want to include in our first version.

Now that we have finished conceptualizing our app, we are ready to start coding. In Chapter 3, One Piece at a Time – Types, Scopes, and Projects, we created a command-line project. This time, we are going to create an iOS Application. Once again, in Xcode, navigate to File | New | Project…. When a window appears, select the Single View Application from the iOS | Application menu:

Setting up the app project

From there, click on Next and then give the project the name LearningCamera. Any Organization Name and Identifier are fine. Finally, make sure that Swift is selected from the Language drop down menu and Universal is selected from the Devices drop down. Now select Next again and create the project.

Xcode will then present you with a project development window that looks somewhat different from a command-line project:

Setting up the app project

This default screen allows us to configure various attributes of the app including the version number, target devices, and much more. For our purposes, all of the defaults are fine. When you decide to submit an app to the app store, this screen will become much more important.

Xcode has also created a few different files and folders for us. We will be working exclusively in the LearningCamera folder. The LearningCameraTests folder is for automated tests; they are a fantastic idea but beyond the scope of this book. The final folder is the Products folder, which you won't have to change.

In the LearningCamera folder, we have several important files. The first file is AppDelegate.swift, which is the entry point of the application. It has a class that was created for you, called AppDelegate that has a number of methods that are called at different points during the application life cycle. We won't have to modify this file for our purposes but it is an important file in many applications.

The second file is ViewController.swift. This holds a UIViewController subclass that is used to manage the interaction between the app's default view and the business logic. We will be doing a lot of work in there.

The third file is Main.storyboard. This file contains the interface design for our views. Currently, it has only a single view that is managed by ViewController. We will be working with this file later to add and configure our visual components.

The fourth file is Assets.xcassets. This is a container for all of the images that we would want to display in our app. Almost every app you make will have at least one image so this is a very important file too.

Finally, the last file is LaunchScreen.storyboard. This file lets us manage the display while our app is launching. This is an extremely important part of a production application because this is the first thing a user sees every time they launch it; a well-designed launch process can make a huge difference. However, we do not have to do anything to this file for our learning purpose.

Now that we have our bearings within the project, let's jump into configuring the user interface of our app. As we discussed earlier, this is done within the Main.storyboard file. When we select that file, we are presented with a graphical editing tool, generally referred to as Interface Builder:

Configuring the user interface

In the center, there is a main view that is controlled by a ViewController instance. This is a blank canvas where we can add all of the interface elements we want.

The first thing we want to do is add the bar along the top that is in our wireframes. This bar is called a navigation bar and we can add it directly, as it is one of the elements in our library. However, the frameworks will handle many complications for us if we use a Navigation Controller instead. A Navigation Controller is a view controller that contains other view controllers. Specifically, it adds a navigation bar to the top and allows us to push child view controllers onto it in the future. This controller creates the animation of a view being pushed on from the right in many applications. For example, when you select an e-mail in the Mail app, it animates in the contents of the e-mail; this uses a navigation controller. We will not have to push any view controllers on in this app, but it is good to be set up for the future and this is a superior way of getting a navigation bar at the top.

Along the right, we have a library of elements we can drag onto the canvas, let's start by finding the Navigation Controller. Drag it from the library to the pane on the left where the View Controller Scene is listed. This is going to add two new view controllers to the list:

Configuring the user interface

We don't want the new Root View Controller, only the View Controller Scene so let's delete it. To do this, click on the Root View Controller with the yellow icon and press the Delete key. Next, we want to make the View Controller Scene the root view controller. The root view controller is the first controller to be shown within the Navigation Controller. To do this, right-click on the Navigation Controller with the yellow icon and drag it to the View Controller with the yellow icon below. The View Controller will be highlighted blue:

Configuring the user interface

Once you let go of the right mouse button, a menu will come up and you should click on Root View Controller. Finally, we want to make the navigation controller the first view controller to appear in the app. Select the Navigation Controller with the yellow icon and navigate to View | Utilities | Show Attributes Inspector from the main menu, and then scroll-down and check the Is Initial View Controller checkbox. Note that you can drag around the view controllers on the screen however, you want to make the file easier to navigate.

Now we are ready to customize our main view. To focus the view, select View Controller from the pane on the left. Now double-click on the title and change it to Gallery:

Configuring the user interface

Next, we want to add the "Take a Picture" button to our navigation bar. All buttons in toolbars are called bar button items. Find them in the library and then drag it to the right side of the toolbar (the place where you can drop it will turn blue when you get close to it). By default, the button will say Item, but we want it to be an add button instead. One option would be to change the text to an addition symbol, but there is a better option. After adding the button, you should be able to see it appear in the hierarchy that is to the left of the main view. In there, you will see the navigation bar with the new button item nested inside the Gallery title. If you select that item in the hierarchy, you will see some options we can configure about the item along the right-hand side of the screen. We want to change the System Item to Add:

Configuring the user interface

Now, you can do the same thing for the left-hand side of the navigation bar with the Edit identifier.

Finally, we need to add the gallery of photos. For this, we are going to use the Collection View from the library. Drag one onto the center of the view. A collection view is made of a variable amount of cells laid out in a grid. Each cell is a copy of a template cell and it can be configured in code to display specific data. When you dragged the collection view on, it also created a template cell for you. We will configure that soon.

First, we need to define the rules for the sizing of the collection view. This will allow the interface to adapt well to each different screen size. The tool we use to do this is called Auto Layout. Click on the collection view and then select the Pin icon in the lower right of the screen:

Configuring the user interface

Configure this window to match the preceding screenshot. Click on each of the four struts so that they are highlighted red, uncheck Constrain to margins, and change each of the measurements to zero. After everything is configured, click on Add 4 Constraints. This will cause some yellow lines to appear that indicate that the view's placement is not consistent with the rules we just created. We can resize the views ourselves to make it match or we can let Xcode do it for us: there will be a yellow icon next to the Gallery Scene on the left-hand side of the screen. Click on that and you will get a list of misplaced views. In there, you can click on the yellow triangle and click on Fix Misplacement. We also want to make the background white instead of black. Select the collection view and then change its Background to white in the Attributes Inspector.

The last thing we need to configure on this screen is the collection view cell. This is the box in the upper-left corner of the collection view. We need to change the size and add both an image and a label; let's start by changing the size. Click on the Collection View if it isn't already selected and navigate to View | Utilities | Show Size Inspector from the main menu. Change the Cell Size to be 110 points wide and 150 points tall.

Now, we can drag in our image. In the library, this is called an Image View. Drag it into the cell and then change the height and width in the Size Inspector to 110 and x and y to 0. Next, we want to drag a Label below the image view. Once it is placed, we want to configure the placement rules within the cell.

First, select the Image View. We have to make it the full width and attach it to the top of the cell, so select the pin icon again and configure it as follows:

Configuring the user interface

It is pinned to the left, top, and right without constraining to margins and values of zero for all three measurements. Click on Add 3 Constraints and we are ready to define the rules for the label. We want the label to be full width and vertically centered. A label is going to automatically center the text, so we want the label to be tall enough to have a reasonable margin above and below the text. Click on the label and configure it as follows:

Configuring the user interface

It is pinned in every direction without constraining to the margins and has zero for all measurements. It is also constrained to be 30 points tall by checking the Height checkbox. Click Add 5 Constraints and then have Xcode resize it for you again from the menu on the left. Also, make sure to select the center alignment in the Attributes Inspector and reduce the font size to 12.

Now we are ready to move onto the programming. The first thing we need to allow the user to do is to take a new picture. In order to do that, we are going to need some code to run every time the user taps on the add button. We achieve this by connecting the trigger action of the add button to a method on our view controller. Normally we make a connection by right-click dragging from the button to the code; however, we can't do this if we can't see the interface and the code at the same time. The easiest way to do this is to show the Assistant Editor. You can do this by navigating to View | Assistant Editor | Show Assistant Editor. Also, make sure it is configured to be automatic by clicking on the bar at the top of the editor:

Allowing picture taking

This mode causes the second view to automatically change to the most appropriate file according to what you have selected on the left. In this case, because we are working with the interface of our view controller, it shows the code for the view controller.

Our view controller code is generated with two methods to start. viewDidLoad is called when the view for the view controller is loaded. Most of the time this happens when the view controller is about to be displayed for the first time. didReceiveMemoryWarning is called when the system starts to run low on memory. This provides you an opportunity to help the system find more memory by deleting anything that isn't necessary.

We want to start by creating a connection from the button to a new method. You can do so by right clicking on the add button and dragging to below the didReceiveMemoryWarning method:

Allowing picture taking

When you release the right mouse button, a little window will appear. There you should select Action from the Connection menu and enter didTapTakePhotoButton. When you click on Connect, Xcode will create a new method for you and connect it to the button. You know it is connected because there is a filled in gray circle to the left of the method. Now, every time the user taps the button, this method will be executed. Note that this method has @IBAction at the beginning of it. This is needed for any method that is connected to an interface element.

We want this method to present the user with an interface to take a picture. Apple provides a class for us called UIImagePickerController that makes this very easy for us. All we need to do is create an instance of UIImagePickerController, configure it to allow taking pictures, and present it to the screen. The code looks like this:

Lets break this code down. On the first line, we are creating our image picker. On the second line, we are checking if the current device has a camera by using the isSourceTypeAvailable: class method of UIImagePickerController. If the camera source is available, we set that as the source type for the image picker on line three. Otherwise, by default, an image picker lets the user pick an image from their photo library. Since the simulator doesn't support taking a picture, you are going to be presented with an image picker instead of a camera when simulating the app. Finally, the last line asks our view controller to present our image picker by animating it on the screen. presentViewController:animated:completion: is a method implemented within the UIViewController class, the superclass of our ViewController, to make it easy for us to present new view controllers. If you run the app and click on the add button, you will be asked for permission to access the photos and then it will display the photo picker. You can tap the Cancel button in the upper right and the image picker controller will be dismissed. However, if you select a photo, nothing will happen.

We need to write some code to handle the picking of a photo. To make this possible, image picker can have a delegate that receives a method call when an image is picked. We are going to make our view controller the delegate of the image picker and implement its protocol. First, we have to add a line to our action method above, that assigns our view controller as the delegate of the image picker. Add this line above the call to present the image picker:

When we do that, we will get a compiler error that says that we can't make this assignment because our view controller doesn't implement the necessary protocols. Lets change that. I like to implement each protocol as a separate extension in the same file to allow for better code separation. We need to implement both UIImagePickerControllerDelegate and UINavigationControllerDelegate according to the error. The only method that is important to us in either of these protocols is the one that is called when an image is picked. That leaves us with the following code:

Our implementation for the UINavigationControllerDelegate delegate is empty but we have a simple implementation for the imagePickerController:picker:didFinishPickingImage:editingInfo: method. This is where we are going to add our handling code, but for now, we are just dismissing the presented view controller to return the user to the previous screen. This method does not force us to specify the view controller we are dismissing because the view controller already knows which one it is presenting. Now, if you run the app and select a photo, you will return to the previous screen but nothing else will happen. In order to make something meaningful happen with the photo, we are going to have to put a lot of other code in place. We have to both save the picture and implement our view controller to display the picture inside our collection view.

To start, we are only going to concern ourselves with temporarily storing our pictures in memory. To do this, we can add an image array as a property of our view controller:

As we saw in the image picker delegate method, UIKit provides a class UIImage that can represent images. Our photos property can store an array of these instances. This means that the first step for us is to add new images to our property when the callback is called:

Now every time the user takes or picks a new photo, we add it to our list, which stores all of the images in memory. However, this isn't quite enough, we also want to require a label for each photo.

To support this feature, let's create a new structure called Photo that has an image and label property. At this point, I would create three groups in the LearningCamera folder: Model, View, and Controller by right-clicking on the LearningCamera folder and choosing New Group. I would move ViewController.swift into the Controller group and then create a new Photo.swift file by right-clicking on the Model group and selecting New File…. Just a plain Swift File is fine.

You should define your photo structure in that file:

We have to import UIKit because that is what defines UIImage. The rest of our structure is straightforward as it just defines our two desired properties. The default initializer will be fine for now.

Now, we can return to our ViewController.swift file and update our photos property to be of the type Photo instead of UIImage:

This now creates a new problem for us. How do we ask the user for the label for the image? Let's do that in a standard alert. To display an alert, UIKit has a class called UIAlertController. To use this, we will have to rework our function some. UIKit does not allow you to present more than one view controller from the same view controller at the same time. This means that we have to dismiss the photo picker and wait for that to complete before displaying our alert:

Lets break down this code, as it is somewhat complex. To start, we are using the trailing closure syntax for the dismissViewControllerAnimated:completion: method. This closure is called once the view controller has finished animating off the screen.

Next, we are creating an alert controller with a title, message, and Alert as its style. Before we can display the alert controller, we have to configure it with a text field and a save action. We start by adding the text field and use the trailing closure again on addTextFieldWithConfigurationHandler:. This closure is called to give us an opportunity to configure the text field. We are OK with the defaults but we are going to want to know the text contained in the text field when saving so we can create our save action directly within this alert and save ourselves the hassle of getting a reference to it later.

Each action of an alert must be of the type UIAlertAction. In this case, we create one with the title Save with the default style. The last parameter of the UIAlertAction initializer is a closure that will be called when the user chooses that action. Again, we use the trailing closure syntax.

Inside that callback, we get the text from the text field and use that, along with our image, to create a new Photo instance and add it to our photos array.

Finally, we have to add our save action to the alert controller and then display the alert controller.

Now if you run the app, it will ask you for a label for each photo after it is chosen but it still won't appear to be showing it because we are not displaying the saved photos yet. That is our next task.

Now that we are maintaining a list of photos, we need to display it in our collection view. A collection view is populated by providing it with a data source that implements its UICollectionViewDataSource protocol. Probably the most common thing to do is to have the view controller be the data source. We can do this by opening the Main.storyboard back up and control dragging from the collection view to the view controller:

Populating our photo grid

When you let go, select dataSource from the menu. After that, all we need to do is implement the data source protocol. The two methods we need to implement are collectionView:numberOfItemsInSection: and collectionView:cellForItemAtIndexPath:. The former allows us to specify how many cells should be displayed and the latter allows us to customize each cell for a specific index into our list. It is easy for us to return the number of cells that we want:

All we have to do is return the number of elements in our photos property.

Configuring the cell is going to take a little bit more preparation. First, we need to create our own cell subclass that can reference the image and label we created in the storyboard. All collection view cells must subclass UICollectionViewCell. Let's call ours PhotoCollectionViewCell and create a new file for it in the View group. Like we needed a connection from the storyboard to our code for tapping the add button, we need a connection for both the image and the label. However, this is a different type of connection. Instead of an action, this type of connection is called an outlet, which adds the object as a property to the view controller. We could use the same click and drag technique we used for the action, but this time we will set up the code in advance ourselves:

Here we have specified two properties, each with a prefix of @IBOutlet. This prefix is what allows us to make the connection in Interface Builder just like we did with the data source. Both types are defined as implicitly unwrapped optionals because these connections cannot be set when the instance is initialized. Instead, they are connected when loading the view.

Now that we have that setup, we can go back to the storyboard and make the connections. Currently the cell is still just the type of a generic cell so first we need to change it to our class. Find the cell inside the view hierarchy on the left and click on it. Select View | Utilities | Show Identify Inspector. In this inspection, we can set the class of the cell to our class by entering PhotoCollectionViewCell in the class field. Now if you navigate to View | Utilities | Show Connections Inspector you will see our two outlets listed as possible connections. Click and drag from the hollow gray circle next to imageView to the image view in the cell:

Populating our photo grid

Once you let go, the connection will be made. Do the same thing with the label connection to the label we created before. We also need to set a reuse identifier for our cell so that we can reference this template in code. You can do this by returning to the Attributes Inspector and entering DefaultCell into the Identifier text field:

Populating our photo grid

We are also going to need a reference to the collection view from within our view controller. This is because we will need to ask the collection view to add a cell each time a photo is saved. You can add this by writing the code first or by right clicking and dragging from the collection view to the code. Either way, you should end up with a property like this on the view controller:

Then we are ready to implement the remaining data source method:

The first line of this implementation asks the collection view for a cell with our DefaultCell identifier. To understand this fully, we have to understand a little bit more about how a collection view works. A collection view is designed to handle virtually any number of cells. We could want to display thousands of cells at once but it would not be possible to have thousands of cells in memory at one time. Instead, the collection view will automatically reuse cells that have been scrolled off the screen to save on memory. We have no way of knowing whether the cell we get back from this call is new or reused, so we must always assume it is being reused. This means that anything we configure on a cell in this method, must always be reset on each call, otherwise, some old configurations may still exist from its previous configuration. We end that call by casting the result to our PhotoCollectionViewCell class so that we can configure our subviews properly.

Our second line is getting the correct photo out of our list. The item property on the indexPath variable is the index of the photo that we are using to configure the cell. At any time, this method could be called with any index between zero and the number returned in our previous data source method. This means that in our case, it will always be a number within our photos array, making it safe to assume that the index is properly within its bounds.

The next two lines set the image and label according to the photo and finally, the last line returns that cell so that the collection view can display it.

At this point, if you ran the app and added a photo you still wouldn't see anything because the collection view will not automatically reload its data when an element is added to the photos array. That is because the collectionView:numberOfItemsInSection: method is a callback. Callbacks are only called when other code initiates it. This method is called once when the collection view is first loaded but we must ask it to be called again manually from then on. The easiest way to do this is to call reloadData on the collection view when we add a photo to the list. This causes all of the data and cells to be loaded again. However, this does not look very good because the cell will just pop into existence. Instead, we want to use the insertItemsAtIndexPaths method. When used properly, this will cause a cell to be animated onto the screen. The important thing to remember with this method is that you must only call it after collectionView:numberOfItemsInSection: returns the updated amount after the insertion. This means we must call it after we have already added our photo to our property:

Only the last two lines of this are new. First, we create an index path for where we want to insert our new item. An index path consists of both an item and a section. All of our items exist in a single section, so we can always set that to zero. We want the item to be one less than the total count of photos because we just added it to the end of the list. The last line is simply making the call to the insert items method that takes an array of index paths.

Now you can run your app and all saved photos will be displayed in the collection view.

We have already made some good progress on the core functionality of our app. However, before we move any further, we should reflect on the code we have written. Ultimately, we haven't actually written that many lines of code, but it can definitely be improved. The biggest shortcoming of our code is that we have put a lot of business logic inside our view controller. This is not a good separation of our different model, view, and controller layers. Let's take this opportunity to refactor this code into a separate type.

We will create a class called PhotoStore that will be responsible for storing our photos and that will implement the data source protocol. This will mean moving some of our code out of our view controller.

First, we will move the photo's property to the photo store class:

Note that this new photo store class inherits from NSObject. This is necessary for us to be able to fully satisfy the UICollectionViewDataSource protocol, which is our next task.

We could simply move the code from our view controller to this class, but we do not want our model to deal directly with our view layer. The current implementation creates and configures our collection view cell. Lets allow the view controller to still handle that by providing our own callback for when we need a cell for a given photo. To do that, we will first need to add a callback property:

We need to provide an initializer now so that we can get the callback function. Next, we have to tweak our data source implementations and put them in this new class:

The collectionView:numberOfItemsInSection: method can still just return the number of photos in our array, but collectionView:cellForItemAtIndexPath: is implemented to use the callback instead of creating a cell itself.

The second thing we need to add to this class is the ability to save a photo. Let's add a method to take a new image and label that returns the index path that should be added:

This looks identical to the code we wrote in the view controller to do this, but it is better separated.

Now our photo store is complete and we just have to update our view controller to use it instead of our old implementation. First, lets add a photo store property that is an implicitly unwrapped optional in ViewController so we can create it after the view is loaded:

To create our photo store in viewDidLoad, we will call the photo store initializer and pass it a closure that can create the cell. For clarity, we will define that closure as a separate method:

This method looks almost identical to our old collectionView:cellForItemAtIndexPath: implementation; the only difference is that we already have a reference to the correct photo.

This method allows our viewDidLoad implementation to be very simple. All we need to do is initialize the photo store with a reference to this method and make it the data source for the collection view:

Lastly, we just have to update the save action to use the photo store:

You can run the app again and it will operate as before, but now our code is modular, which will make any future changes much easier.

Our app works pretty well for saving pictures, but as soon as the app quits, all of the photos are lost. We need to add a way to save the photos permanently. Our refactoring of the code allows us to work primarily within the model layer now.

Before we write any code, we have to decide how we are going to store the photos permanently. There are many ways in which we can choose to save the photos, but one of the easiest is to save it to the file system, which is what we conceived of in our conception phase. Every app is provided a documents directory that is automatically backed up by the operating system as a part of normal backups. We can store our photos in there as files named after the label the user gives them. To avoid any problems with duplicate labels, where we would have multiple files named the same thing, we can nest every file inside a subdirectory named after the time the photos is saved. The time stamp will always be unique because we will never save two photos at the exact same time.

Now that we have that decided, we can start to update our photo store code. First, we will want to have an easy way to use a consistent directory for saving. We can create that by adding a method called getSaveDirectory. This method can be private and, as a convention, I like to group private code in a private extension:

This code first gets a URL representing the documents directory from an Apple-provided class called NSFileManager. You may notice that NSFileManager has a shared instance that can be accessed through the defaultManager class method. We then call the URLForDirectory method, give it information indicating that we want the documents directory for the current user, and return the result. Note that this method can throw an error, so we marked our own method as throwing and did not allow any errors to propagate.

Now we can move on to saving all added images to disk. There are a number of things that we will need to be done. First, we need to get the current time stamp. We can do this by creating an NSDate instance, asking that for the time stamp and using string interpolation to turn it into a string:

NSDate instances can represent any sort of time on any date. By default, all NSDate instances are created to represent the current time.

Next, we are going to want to append that onto our save directory to get the path where we are going to save the file. For that, we can use the URLByAppendingPathComponent: method of NSURL:

This will ensure that the proper path slash is added, if it is not already there. Now we need to make sure that this directory exists before we try to save a file to it. This is done using a method on NSFileManager:

This method can throw if there is an error, which we will need to handle later. It is still considered a success if the directory already exists. Once we are sure that the directory has been created, we will want to create the path to the specific file using the label text:

Here we used string interpolation to add a .jpg extension to the file name.

Most importantly, we will need to convert our image to data that can be saved to a file. For that, UIKit provides a function called UIImageJPEGRepresentation that takes the UIImage and returns an NSData instance:

The second parameter is a value between zero and one representing the compression quality we want. In this case, we want to save the file at full quality, so we use 1. It then returns an optional data instance, so we will need to handle the scenario where it returns nil.

Finally, we need to save that data to the file path we created:

This method on NSData simply takes the file path and a Boolean indicating if we want it to write to a temporary location before it overwrites any existing file. It also returns true or false depending on if it is successful. Unlike directory creation, this will fail if the file already exists. However, since we are using the current time stamp that should never be a problem.

Lets combine all of this logic into a method on our photo structure that we can use later to save it to disk, which throws an error in case of an error:

First, we define a nested enumeration for our possible errors. Then we define the method to take the root level directory where it should be saved. We allow any errors from the directory creation to propagate. We also need to throw our errors if the data comes back nil or if the writeToURL:automatically: method fails.

Now we need to update our saveNewPhotoWithImage:labeled: to use the saveToDirectory: method. Ultimately, if an error is thrown while saving the photo, we will want to display something to the user. That means that this method will need to just propagate the error, because the model should not be the one to display something to the user. That results in the following code:

If the saving to directory fails, we will skip the rest of the method so we won't add it to our photos list. That means we need to update the view controller code that calls it to handle the error. First, let's add a method to make it easy to display an error with a given title and message:

This method is simple. It just creates an alert with an OK button and then presents it. Next, we can add a function to display any kind of error we will expect. It will take a title for the alert that will pop-up, so we can customize the error we are displaying for the scenario that produced it:

We expect either the built-in error type of NSError that will come from Apple's APIs or the error type we defined in our photo type. The localized description property of Apple's errors just creates a description in the locale the device is currently configured for. We also handle any other error scenarios by just reporting it as an unknown error.

I would also extract our save action creation to a separate method so we don't overcomplicate things when we add in our do-catch blocks. This will be very similar to our previous code but we will wrap the call to saveNewPhotoWithImage:labeled: in a do-catch block and call our error handling method on any thrown errors:

That leaves us with just needing to update the imagePickerController:didFinishPickingImage:editingInfo: method to use our new save action creating method:

That completes the first half of permanently storing our photos. We are now saving the images to disk but that is useless if we don't load them from disk at all.

To load an image from disk, we can use the contentsOfFile: initializer of UIImage that returns an optional image:

To convert our file path URL to a string, which is what the initializer requires, we can use the relative path property.

We can get the label for the photo by removing the file extension and getting the last component of the path:

Now we can combine this logic into an initializer on our Photo struct. To do this, we will also have to create a simple initializer that takes the image and label so that our other code that uses the default initializer still works:

Lastly, we need to have the image store enumerate through the files in the documents directory calling this initializer for each one. To enumerate through a directory, NSFileManager has an enumeratorAtFilePath: method. It returns an enumerator instance that has a nextObject method. Each time it is called, it returns the next file or directory inside the original directory. Note that this will enumerate all children of each subdirectory it finds. This is a great example of the iterator pattern we saw in Chapter 9, Writing Code the Swift Way – Design Patterns and Techniques. We can determine if the current object is a file using the fileAttributes property. All of that lets us write a loadPhotos method like this:

func loadPhotos() throws {
    self.photos.removeAll(keepCapacity: true)
    
    let fileManager = NSFileManager.defaultManager()
    let saveDirectory = try self.getSaveDirectory()
    let enumerator = fileManager.enumeratorAtPath(
        saveDirectory.relativePath!
    )
    while let file = enumerator?.nextObject() as? String {
        let fileType = enumerator!.fileAttributes![NSFileType]
            as! String
        if fileType == NSFileTypeRegular {
            let fullPath = saveDirectory
                .URLByAppendingPathComponent(file)
            if let photo = Photo(filePath: fullPath) {
                self.photos.append(photo)
            }
        }
    }
}

The first thing we do in this method is remove all existing photos. This is to protect against calling this method when there are already photos in it. Next, we create an enumerator from our save directory. Then, we use a while loop to continue to get each next object until there are none left. Inside the loop we check if the object we just got is actually a file. If it is and we create the photo successfully with the full path, we add the photo to our photos array.

Finally, all we have to do is make sure this method is called at the appropriate time to load the photos. A great time to do this, considering we want to be able to show errors to the user, is right before the view will be displayed. As the view controllers have a method for right after the view has been loaded, there is also a method called viewWillAppear: that is called every time the view is about to appear. In here we can load the photos and also display any errors to the user with our displayError:withTitle: method:

Now if you run the app, save some photos, and quit it, your previously saved photos will be there when you run it again. We have completed the saving photos functionality!

This app is far from being something that we could put on the store, but it gives you a good first dive into what it is like to build an iOS app. We have covered how to conceptualize an app and then how to go about making it a reality. We know how to configure an interface in a storyboard, how to run it, and we got into the practical details of saving photos both temporarily and permanently to disk and displaying those in our own custom interface. We even got some practice writing high quality code by ensuring our code sticks with the model-view-controller design pattern as best we can.

Even though we have covered a lot, this clearly isn't enough information to immediately write any other iOS app. The key is to get an insight into what the app development process looks like and to start to feel more comfortable in an iOS app project. All developers spend lots of time searching the documentation and the Internet for how to do specific things on any given platform. The key is being able to take solutions you find on the Internet or in books, determine the best one for your use case, and integrate them effectively into your own code. Over time, you will be able to do more and more on your own without looking it up, but with ever-changing frameworks and platforms, that will always be a part of your development cycle.

With that in mind, I now challenge you to complete the feature list we conceptualized. Figure out how to delete a picture and add whatever other features, usability tweaks, or visual tweaks you want. As I said before, app development is a completely new world to explore. There are so many things that you can tweak, even with this simple app; all of it will help you learn tons.

Coming up in our final chapter, we will look at where you can go from here to become the best Swift developer you possibly can.