Design Patterns on iOS using Swift – Part 1/2

In the first half of this two-part tutorial, you’ll learn about common design patterns when building iOS apps, and how to apply these patterns in your own apps. By Lorenzo Boaro.

Leave a rating/review
Save for later
Share
You are currently viewing page 4 of 4 of this article. Click here to view the first page.

How to Use the Delegate Pattern

Open up ViewController.swift and add these private properties to the class:

private var currentAlbumIndex = 0
private var currentAlbumData: [AlbumData]?
private var allAlbums = [Album]()

Starting from Swift 4, variables marked as private can share the same access control scope between a type and any extension on said type. If you want to browse the new features introduced by Swift 4, take a look to What’s New in Swift 4?.

You’re going to make ViewController the table view’s data source. Add this extension to the end of ViewController.swift, after the closing brace of the class definition:

extension ViewController: UITableViewDataSource {

}

The compiler will warn you because UITableViewDataSource has a few mandatory functions. Add the following code inside the extension to make it happy:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  guard let albumData = currentAlbumData else {
    return 0
  }
  return albumData.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
  if let albumData = currentAlbumData {
    let row = indexPath.row
    cell.textLabel!.text = albumData[row].title
    cell.detailTextLabel!.text = albumData[row].value
  }
  return cell
}

tableView(_:numberOfRowsInSection:) returns the number of rows to display in the table view, which matches the number of items in the “decorated” representation of the album.

tableView(_:cellForRowAtIndexPath:) creates and returns a cell with the title and its value.

Note: You can actually add the methods to the main class declaration or to the extension; the compiler doesn’t care that the data source methods are actually inside the UITableViewDataSource extension. For humans reading the code though, this kind of organization really helps with readability.

Next, replace viewDidLoad() with this code:

override func viewDidLoad() {
  super.viewDidLoad()
 
  //1
  allAlbums = LibraryAPI.shared.getAlbums()

  //2
  tableView.dataSource = self		
}

Here’s a breakdown of the above code:

  1. Get a list of all the albums via the API. Remember, the plan is to use the facade of LibraryAPI rather than PersistencyManager directly!
  2. This is where you setup the UITableView. You declare that the view controller is the UITableView data source; therefore, all the information required by UITableView will be provided by the view controller. Note that you can actually set the delegate and datasource in a storyboard, if your table view is created there.

Now, add the following method to the ViewController class:

private func showDataForAlbum(at index: Int) {
    
  // defensive code: make sure the requested index is lower than the amount of albums
  if (index < allAlbums.count && index > -1) {
    // fetch the album
    let album = allAlbums[index]
    // save the albums data to present it later in the tableview
    currentAlbumData = album.tableRepresentation
  } else {
    currentAlbumData = nil
  }
  // we have the data we need, let's refresh our tableview
  tableView.reloadData()
}

showDataForAlbum(at:) fetches the required album data from the array of albums. When you want to present the new data, you just need to call reloadData on the UITableView. This causes the table view to ask its data source such things as how many sections should appear in the table view, how many rows in each section, and how each cell should look, etc.

Add the following line to the end of viewDidLoad()

showDataForAlbum(at: currentAlbumIndex)

This loads the current album at app launch. And since currentAlbumIndex is set to 0, this shows the first album in the collection.

Build and run your project. Your app should start and present you with the following screen:

Album app showing populated table view

Table view data source success!

Final Touches

In order to not pollute your code with hardcoded values, like the string Cell, go to ViewController and, just after the opening brace of the class definition, add the following:

private enum Constants {
  static let CellIdentifier = "Cell"
}

Here you are creating an enumeration that acts as a container for your constants.

Note: The advantage of using a case-less enumeration is that it can’t accidentally be instantiated and works as a pure namespace.

Now just replace "Cell" with Constants.CellIdentifier.

Where to go from here?

Things are looking pretty good so far! You have the MVC pattern in place, and you’ve also seen the singleton, facade, and decorator patterns in action. You can see how these are used within Cocoa by Apple, and also how to apply the patterns to your own code.

Here is the final project for this part if you want to have a look or compare.

There’s a lot more in store: there are still the adapter, observer, and memento patterns to cover in part two of this tutorial. And if that’s not enough, we have a follow-up tutorial coming up covering even more design patterns as you work on refactoring a simple iOS game.

If you have questions or just want to talk about your favorite design patterns, join in on the forum discussion below!