It’s been a while since I gave developing an iOS app a try. With all the extra time we had on our hands during the quarantine, I decided to dig in and see what was new. Here’s what I learned while building an app to read Hacker News. You can take a look at the source code on GitHub.

Screenshot of Read HN on iPad

The Coordinator Pattern

This is a great abstraction and one that was definitely needed. When developing iOS apps, View Controllers quickly become monsters with lots of different responsibilities crammed into a single place. Coordinators pull the logic around navigation and provide the necessary data up a level.

My playground app only had one coordinator which managed the navigation between the list of stories and the detail controller. It also handled setting up the API listeners. Coordinators are a missing link in the world of iOS development.

Auto Layout and Constraints with Outlets

Auto Layout was initially harder to understand than I expected. Now I'm a fan. I now want CSS to work the same way. It’s powerful and flexible at the same time. It allows interfaces to elegantly expand and contract across the different layouts available in iOS.

Setting an IBOutlet for a constraint was a nice idea as well. I wanted to create a custom UITableViewCell with some additional information beyond a title and a subtitle. I also wanted the indentation that comes with the standard cells. By adding an outlet, I was able to set the offset as the cells were rendered to achieve the same effect based on the device size.

Dispatch Groups

My app used the Firebase Realtime Database to receive updates from Hacker News. Streaming APIs are great for quick updates but can wreak havoc on a user interface. I didn’t want things bouncing around all over the place when updates came in. Dispatch Groups are a great way to wait for a batch of requests to finish before making an update.

The API is super simple: create a group, enter it when you start a request, leave on completion, and assign a notifier to do something when the group is empty.

Notifications and Observers

Notifications and Observers are useful when you need to let different areas of the application know when something has happened. For example, if a user changes the font size in settings, or if a long running network process has finished.

I used notifications to post updates when new stories arrived, or a batch of comments had been downloaded. You can register an observer for a certain notification type and act on the information as necessary.

Closures, Delegates and Protocols, and Notifications

There are (at least) three different ways of communicating in iOS development: closures, delegates and protocols, and notifications and observers. When should you use each?

It’s all about distance. The more closely two objects are related, the more you can share between them. Closures and completion handlers make sense in the same Class or file, for example.

Swift is a Great Language

I’ve built complex apps in Objective-C. It’s been a while though. I’ve seen Swift and even dabbled a bit. Using Swift “in anger” was a fantastic experience. Coming from lots of Ruby development, the syntax is enjoyable. I’d go so far as to say it might even be a bit better! The addition of type checking is a welcome one, saving myself from myself. No semicolons or unnecessary ceremony, either.

Hard Stuff is Easier

I’m not sure if it is just because of the reduced word count from Objective-C to Swift. No, it’s definitely not. Network requests and JSON parsing used to be a chore. Not anymore! Working with APIs and the addition of Codable make working with external data a piece of cake. Fetching data with URLSession is super simple as well.

I’m glad to see CocoaPods are still alive and growing but I made a conscious effort to stay within the confines of UIKit and Apple frameworks for this side project. The only Pod I added was for Firebase. It’s nice to be able to accomplish so much without adding external dependencies.

Playgrounds

This is a bit of a bonus. I used a playground a few times just to get more familiar with the language. Having a quick REPL available to test an idea or flesh out a function without the mental overhead of your full project is really nice. Being able to use UIKit in them is fantastic.

I’ve also been working with my kids using the Swift Playgrounds app on an old MacBook Air. It’s fun to see them pick up the same language that they could use to build an iOS app a bit later!

Bonus: SwiftUI

Storyboards are great. Writing UI in code is great. What’s better? Being able to use either or both. SwiftUI is exciting to me because it blends the two ideas seamlessly. Making a code change immediately updates the UI and using the graphical tool updates the code. I love working with a live preview of my app's UI in multiple states. Extracting a component to be reused in another area of the application is simple as well.

The most significant example so far is the migration from UITableView to List. Here’s some boilerplate code for creating a UITableView:

class ThingsController: UIViewController, UITableViewDelegate, UITableViewDataSource {
  override func viewDidLoad() {
    tableView.dataSource = self tableView.delegate = self
  }

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let rowNumber = indexPath.row let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! StoryCell
    cell.story = dataSource?.stories[rowNumber]
    return cell
  }

  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return dataSource?.stories.count ?? 0
  }

  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    if let story = dataSource?.stories[indexPath.row] {
      itemsDelegate?.loadComments(story: story)
      self.collapseDetailViewController = false
    }
  }
}

Here’s the same code in SwiftUI:

struct ThingsListView: View {
    @Binding var things: [Thing]

    var body: some View {
        List {
            ForEach(things, id: .self) { thing in
                NavigationLink(
                    destination: DetailView(thing: thing)
                ) {
                    Text(thing.title)
                }
            }
        }
    }
}

If you want to learn more about SwiftUI's capabilities (as of 2019), Introducing SwiftUI: Building Your First App from WWDC 2019 is a fantastic introduction.

Credits