© Stefan Kaczmarek, Brad Lees, Gary Bennett 2019
Stefan Kaczmarek, Brad Lees and Gary BennettSwift 5 for Absolute Beginnershttps://doi.org/10.1007/978-1-4842-4868-3_14

14. A Swift iPhone App

Stefan Kaczmarek1 , Brad Lees1 and Gary Bennett2
(1)
Phoenix, AZ, USA
(2)
Scottsdale, AZ, USA
 

In Chapter 8, you created a basic bookstore iPhone app with Swift. In this chapter, you will add some features to the app to make it a bit more functional and use many of the technologies you have learned in this book, such as creating a class, using delegates and protocols, and using actions and outlets. You will also learn about some new techniques such as switches, UIAlertController, and landmarks.

Let’s Get Started

The bookstore example in Chapter 8 enabled you to view books in your bookstore in a table view and then tap the book to see its details. In this chapter, you will add the following capabilities to the Chapter 8 bookstore app:
  • Adding a book

  • Deleting a book

  • Modifying a book

See Figures 14-1 and 14-2.
../images/341013_5_En_14_Chapter/341013_5_En_14_Fig1_HTML.jpg
Figure 14-1.

Adding book functionality

../images/341013_5_En_14_Chapter/341013_5_En_14_Fig2_HTML.jpg
Figure 14-2.

Adding edit and delete functionality along with using a UISwitch

Users will be able to add books to their BookStore using a new AddBookViewController. First, add the code in Listing 14-1 to the prepareForSegue method in the MasterViewController.swift file.
42     // MARK: - Segues
43
44     override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
45         if segue.identifier == "showDetail" {
46             if let indexPath = tableView.indexPathForSelectedRow {
47                 let selectedBook: Book = myBookStore.bookList[indexPath.row]
48                 let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController
49                 controller.detailItem = selectedBook
50                 controller.delegate = self
51                 controller.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem
52                 controller.navigationItem.leftItemsSupplementBackButton = true
53             }
54         } else if segue.identifier == "addBookSegue" {
55             let vc = segue.destination as! AddBookViewController
56             vc.delegate = self
57         }
58     }
Listing 14-1

The prepareForSegue Method

Note

Something that can help organize your code can be found on line 42. "// MARK: - Segues". // MARK: is called a landmark. It is replacement of the #pragma mark, which is used in Objective-C. Landmarks help break up the code in the jump bar and enable you to quickly get to sections of code indicated by the landmark. When you type something following // MARK:, Xcode places the landmark in the jump bar’s drop-down, as shown in Figure 14-3. If you just type // MARK: -, Xcode adds a line separator in the jump bar’s drop-down. Swift also supports // TODO: and // FIXME: landmarks to annotate your code and list them in the jump bar.

../images/341013_5_En_14_Chapter/341013_5_En_14_Fig3_HTML.jpg
Figure 14-3.

Swift’s landmarks

Now add the new view controller AddBookViewController mentioned on line 53 in Listing 14-1. Add a View Controller object to the storyboard by dragging a View Controller to the Main.storyboard file. Then add the objects in Figure 14-4 to enable the user to add a new book. Feel free to move the scene around to make it clear how they relate to each other, as shown in Figure 14-4.
../images/341013_5_En_14_Chapter/341013_5_En_14_Fig4_HTML.jpg
Figure 14-4.

Adding the AddBookViewController and objects

Drag a Bar Button Item from the Object Library to the top-right corner of the MasterViewController’s Navigation Bar, as shown in Figure 14-5.
../images/341013_5_En_14_Chapter/341013_5_En_14_Fig5_HTML.jpg
Figure 14-5.

Adding a Bar Button Item to the MasterViewController

Next, select the newly added Bar Button Item, select the Attributes Inspector, and change the System Item from Custom to Add, as shown in Figure 14-6.
../images/341013_5_En_14_Chapter/341013_5_En_14_Fig6_HTML.jpg
Figure 14-6.

Changing the Bar Button Item from Custom to Add

Add a Show Segue object from the Add Button Bar Item to the new View Controller by Control-dragging or right-clicking and dragging from the Add Button Bar Item to the new View Controller, as shown in Figure 14-7.
../images/341013_5_En_14_Chapter/341013_5_En_14_Fig7_HTML.jpg
Figure 14-7.

Add a Show Segue object to the new View Controller

Modify the insertNewObject function in the MasterViewController.swift, as shown in Listing 14-2.
35     @objc
36     func insertNewObject(_ sender: Any) {
37         performSegue(withIdentifier: "addBookSegue", sender: nil)
38     }
Listing 14-2

insertNewObject Function

Identify the Segue object by clicking the segue arrow and setting the identifier to addBookSegue, as shown in Figure 14-8.
../images/341013_5_En_14_Chapter/341013_5_En_14_Fig8_HTML.jpg
Figure 14-8.

Naming the Segue addBookSegue

After setting the segue identifier, select the AddBookViewController scene and click the Resolve Auto Layout Issues button in the bottom-right corner of Xcode, and select Add Missing Constraints as shown in Figure 14-9.
../images/341013_5_En_14_Chapter/341013_5_En_14_Fig9_HTML.jpg
Figure 14-9.

Adding Missing Constraints

Now you need to create a Swift class to go with the new View Controller. Create a new iOS Cocoa Touch class file and name it AddBookViewController, as shown in Figure 14-10. Make sure you select a subclass of UIViewController.
../images/341013_5_En_14_Chapter/341013_5_En_14_Fig10_HTML.jpg
Figure 14-10.

Adding the AddBookViewController class

Now you have to associate the new AddBookViewController class to the new View Controller. Select the View Controller in the Main.storyboard, and in the Identity Inspector, type AddBookViewController for the class, as shown in Figure 14-11.
../images/341013_5_En_14_Chapter/341013_5_En_14_Fig11_HTML.jpg
Figure 14-11.

Associating the AddBookViewController class to the new View Controller

Next, select the Attributes Inspector and change the Title of the View Controller to Add Book, as shown in Figure 14-12.
../images/341013_5_En_14_Chapter/341013_5_En_14_Fig12_HTML.jpg
Figure 14-12.

Changing the Title of the View Controller to Add Book

Open the AddBookViewController.swift file and add the code shown in Listing 14-3.
9 import UIKit
10
11 protocol BookStoreDelegate {
12     func newBook(_ controller: AnyObject, newBook: Book)
13     func editBook(_ controller: AnyObject, editBook: Book)
14     func deleteBook(_ controller: AnyObject)
15 }
16
17 class AddBookViewController: UIViewController {
18     var book = Book()
19     var delegate: BookStoreDelegate?
20     var read = false
21     var editBook = false
22
23     @IBOutlet weak var titleText: UITextField!
24     @IBOutlet weak var authorText: UITextField!
25     @IBOutlet weak var pagesText: UITextField!
26     @IBOutlet weak var switchOutlet: UISwitch!
27
28     @IBOutlet weak var descriptionText: UITextView!
29
30     override func viewDidLoad() {
31         super.viewDidLoad()
32         if editBook == true {
33             self.title = "Edit Book"
34             titleText.text = book.title
35             authorText.text = book.author
36             pagesText.text = String(book.pages)
37             descriptionText.text = book.description
38             if book.readThisBook {
39                 switchOutlet.isOn = true
40             }
41             else {
42                 switchOutlet.isOn = false
43             }
44         }
45
46         // Do any additional setup after loading the view.
47     }
48
49     override func didReceiveMemoryWarning() {
50         super.didReceiveMemoryWarning()
51         // Dispose of any resources that can be recreated.
52     }
53
54     @IBAction func saveBookAction(_ sender: UIButton) {
55         book.title = titleText.text!
56         book.author = authorText.text!
57         book.description = descriptionText.text
58         if let text = pagesText.text, let pages = Int(text) {
59             book.pages = pages
60         }
61         if switchOutlet.isOn {
62             book.readThisBook = true
63         } else {
64             book.readThisBook = false
65         }
66         if (editBook) {
67             delegate?.editBook(self, editBook:book)
68         } else {
69             delegate?.newBook(self, newBook:book)
70         }
71     }
72 }
Listing 14-3

The AddBookViewController.swift File

To the Book class, add two properties: pages and readThisBook. These are shown in lines 15 and 16 in Listing 14-4.
11 class Book {
12     var title: String = ""
13     var author: String = ""
14     var description: String = ""
15     var pages: Int = 0
16     var readThisBook: Bool = false
17 }
Listing 14-4

Book Class Changes

Switches

Connect the outlets in the AddBookViewController class by dragging them from their open circles to the controls, as shown in Figure 14-13.
../images/341013_5_En_14_Chapter/341013_5_En_14_Fig13_HTML.jpg
Figure 14-13.

Connecting the outlets

Connect the saveBookAction action by dragging the outlet circle to the Save Book button, as shown in Figure 14-14.
../images/341013_5_En_14_Chapter/341013_5_En_14_Fig14_HTML.jpg
Figure 14-14.

Connecting the saveBookAction

In the DetailViewController class , add the code shown in Listing 14-5 above the configureView method.
17     @IBOutlet weak var pagesLabel: UILabel!
18     @IBOutlet weak var readSwitch: UISwitch!
19
20     var delegate: BookStoreDelegate? = nil
21
22     var myBook = Book()
Listing 14-5

New Properties

Alert Controllers

Add the controls for Pages, Read, and Edit for the DetailViewController. Connect the outlets by dragging the open circles to their controls, as shown in Figure 14-15.
../images/341013_5_En_14_Chapter/341013_5_En_14_Fig15_HTML.jpg
Figure 14-15.

Adding the Pages and Read outlets

The Read switch is disabled in this view by unchecking the Enabled property in the Attributes Inspector.

Add the code for displaying a UIAlertController when the Delete button is tapped on the DetailViewController, as shown in Listing 14-6.
47     @IBAction func deleteBookAction(_ sender: UIBarButtonItem) {
48         let alertController = UIAlertController(title: "Warning",message:
49                                                 "Delete this book?",
50                                                 preferredStyle: .alert)
51
52         let noAction = UIAlertAction(title: "No", style: .cancel) { (action) in
53             print("Cancel")
54         }
55         alertController.addAction(noAction)
56
57         let yesAction = UIAlertAction(title: "Yes", style: .destructive) { (action) in
58             self.delegate?.deleteBook(self)
59         }
60         alertController.addAction(yesAction)
61
62         present(alertController, animated: false, completion: nil)
63     }
Listing 14-6

Displaying a UIAlertController

Add the Delete Bar Button Item to the right navigation location and connect it to the action, as shown in Figure 14-16.
../images/341013_5_En_14_Chapter/341013_5_En_14_Fig16_HTML.jpg
Figure 14-16.

Adding the Delete Right Bar Button Item and action

The UIAlertController will warn the user that the book currently displayed in the DetailViewController is about to be deleted and will enable the user to decide whether to delete it. The UIAlertController has two buttons: Yes and No. When the user taps the right Bar Button Item (Delete), the UIAlertController will be as shown in Figure 14-17 when you are finished.
../images/341013_5_En_14_Chapter/341013_5_En_14_Fig17_HTML.jpg
Figure 14-17.

UIAlertController being displayed

When the user taps Yes to delete the book, you want to call the deleteBook delegate method as described in the MasterViewController class. Add the BookStoreDelegate as shown in Listing 14-7.
11 class MasterViewController: UITableViewController, BookStoreDelegate {
Listing 14-7

Adding the BookStoreDelegate

Let’s now talk about the three delegate methods: newBook, deleteBook, and editBook, as defined in the AddBookViewController class in Listing 14-3 (lines 11–15). Add these three methods at the end of the MasterViewController class, as shown in Listing 14-8.
 90     // MARK: - BookStoreDelegate Methods
 91
 92     func newBook(_ controller: AnyObject,newBook: Book) {
 93         myBookStore.bookList.append(newBook)
 94         tableView.reloadData()
 95         navigationController?.popToRootViewController(animated: true)
 96     }
 97
 98     func deleteBook(_ controller: AnyObject){
 99         if let row = tableView.indexPathForSelectedRow?.row {
100             myBookStore.bookList.remove(at: row)
101         }
102         tableView.reloadData()
103         navigationController?.popToRootViewController(animated: true)
104     }
105
106     func editBook(_ controller: AnyObject, editBook: Book){
107         if let row = tableView.indexPathForSelectedRow?.row {
108             myBookStore.bookList[row] = editBook
109         }
110         tableView.reloadData()
111         navigationController?.popToRootViewController(animated: true)
112     }
Listing 14-8

Conforming to the Protocol

The newBook method adds a new book to the bookstore by appending newBook to the bookList array, as shown in line 92. Line 93 then reloads the Table View by calling all the Table View delegate methods:
numberOfSectionsInTableView
numberOfRowsInSection
cellForRowAtIndexPath

Finally, you pop the DetailViewController from the navigation stack by calling popToRootViewController. Popping the view from the navigation stack means the view is removed, similar to tapping the Back button.

The deleteBook method removes the book from the bookList array. First, you determine which row was selected in the tableView and use that index to delete the book in the array by calling remove(at:), as shown on line 99.

The editBook method enables the user to edit an existing book in the bookList array. To do this, the method replaces the edited book in the array at the row that was selected, as shown on line 107.

Now move the Add Book scene down next to the Detail scene and add a Show Segue from the Edit button to the AddBookViewController, as shown in Figure 14-18.
../images/341013_5_En_14_Chapter/341013_5_En_14_Fig18_HTML.jpg
Figure 14-18.

Adding the Edit Show Segue

Select the Segue you just created, select the Attributes Inspector, and name the identifier editDetail. See Figure 14-19.
../images/341013_5_En_14_Chapter/341013_5_En_14_Fig19_HTML.jpg
Figure 14-19.

Naming the Segue’s identifier

In the DetailViewController, add the prepareForSegue method before the configureView method, as shown in Listing 14-9.
24     override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
25         if segue.identifier == "editDetail" {
26             if let vc = segue.destination as? AddBookViewController {
27                 vc.delegate = delegate
28                 vc.editBook = true
29                 vc.book = myBook
30             }
31         }
32     }
Listing 14-9

Add the prepareForSegue Method

Finally, modify the configureView function in the DetailViewController to properly populate the Pages and Read switch outlets, as shown in Listing 14-10.
34     func configureView() {
35         if let detail = self.detailItem {
36             myBook = detail
37             titleLabel.text = myBook.title
38             authorLabel.text = myBook.author
39             descriptionTextView.text = myBook.description
40             pagesLabel.text = String(myBook.pages)
41             if myBook.readThisBook {
42                 readSwitch.isOn = true
43             }
44             else {
45                 readSwitch.isOn = false
46             }
47         }
48     }
Listing 14-10

Modify the configureView

App Summary

Compile and run the app. You should set breakpoints at the delegate functions to watch the program flow. It is a great app to see how delegates can be used to pass information from one view to another.

Additionally, you can add functionality to the app to make the information persistent by using Core Data or UserDefaults.

Exercises

  • Add more books to the bookstore using the original program as a guide.

  • Enhance the Book class so it can store another attribute—a price or ISBN, for example.

  • Add persistence to the app by using Core Data or UserDefaults.