Completing the directory app

In this homework assignment you will add some additional features to the directory app that I showed in class.

The new features include:

The instructions below will walk you step by step through the process of adding these new features.

Adding code to save the directory

The first simple change will make it possible to save the directory to a file and reload the directory from the file when the application starts up. To do this, we need to modify both the Person struct and the Directory class.

Start by updating the line the reads

struct Person {

in the file Person.swift to instead read

class Person : Codable {

This change will do two things. First, it makes the Person class inherit the Codable protocol. This is a prerequisite to being able to encode Person objects into JSON and decode them back from JSON. This is necessary because the mechanism we will use to save the objects to a file saves the data in the form of a JSON text file. Secondly, we need to change Person from a struct to a class because we are going to be passing Person objects to a second view for editing. Classes get passed by reference, which is what we want to make this work correctly.

Next, open Direct.swift and replace the Directory class with this code:

class Directory {
    var people : [Person]

    let itemArchiveURL: URL = {
        let documentsDirectories =
            FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        let documentDirectory = documentsDirectories.first!
        return documentDirectory.appendingPathComponent("directory.plist")
    }()

    init() {
        do {
            let data = try Data(contentsOf: itemArchiveURL)
            let unarchiver = PropertyListDecoder()
            let loc = try unarchiver.decode(Array<Person>.self, from: data)
            people =  loc
        } catch {
            people =
            [Person(name:"Acacia Ackles",office:"Steitz 131"),
             Person(name:"Joe Gregg",office:"Briggs 413"),
             Person(name:"Kurt Krebsbach",office:"Briggs 411")
             ]
        }
    }

    func saveChanges() {
        do {
            let encoder = PropertyListEncoder()
            let data = try encoder.encode(people)
            try data.write(to: itemArchiveURL)
        } catch {
        }
    }

}

This code adds code in the init() function to load the directory data from a file if that file is present and a saveChanges() function that we can call to save our changes back out to the file.

Setting up a Navigation Controller and a second view

To make it possible for users to add new entries to the directory we are going to put the original view for the app in a navigation controller and give users a + button in the navigation bar that will allow them to navigate to a second view where they can enter the details for a new directory entry.

Here are steps needed to set up the navigation controller and the second view.

1) Select the view that displays the directory list and then click the icon at the bottom of the pane and select the option to embed the view in a Navigation Controller.

2) Putting the view in a navigation controller puts a navigation bar at the top of the view. Open the library and drag a Bar Button Item over to the right side of the navigation bar. Set the title for the bar item to +.

3) Add a new view to the storyboard by dragging a View Controller from the library.

4) The new view will need a class behind it to act as the controller for the new view. Add a new file to the project named 'PersonController.swift', and put this code in it:

import UIKit

class PersonController : UIViewController {
    var person : Person!
}

Also, select the new view in the storyboard and click the icon to bring up the identity inspector for the view. Select PersonController from the class pull-down menu to link the PersonController class to the new view.

5) In the storyboard set up the user interface for this new view with Name and Office labels and two text fields. Don't forget to add outlets for the two text fields in the PersonController class and link the outlets to the text fields.

Managing segues between the two views

Next, we need some way to navigate from the directory list to the view that allows us to enter the details for a new entry. We do this by setting up a segue from one view to the other.

To set up the segue, hold down the control key and click on + button you added to the navigation bar. Drag from that button to the second view to set up the segue. You should now see something like this in the storyboard:

The arrow with the circle in the middle represents the newly added segue. Click on the circle to select the segue and set its Identifier property to 'AddPerson'.

When the user triggers the segue by clicking the + button we will want to run some code in a prepare(for:sender:) method in the ViewController class. You can read about this function in the section on passing data around in chapter 11 of the text. The prepare method you set up should create a new Person object, add it to the Directory, pass a reference to the new Person object to the PersonController, and then use this code to add a new row to the table view:

let indexPath = IndexPath(row: directory.people.count-1, section: 0)
tableView.insertRows(at: [indexPath], with: .automatic)

When the user clicks the Back button in the second view to navigate back to the directory list view we will need to run some code that gets the text from the text fields and puts that text into the person object. You can do this by adding a viewWillDisappear(_:) method to the PersonController class. You can read about this method in the section on appearing and disappearing views in chapter 12 of the text. Likewise, you will also need a viewWillAppear(_:) method in the ViewController class that contains code to save the changes to the directory and also does

tableView.reloadData()

to tell the table view to redraw itself to show the new contents.

Supporting editing gestures

One last feature that we will want to support is swiping to delete entries in the directory. You can see an example of how to implement this feature in the section on deleting rows in chapter nine of the textbook.