Coordinator Tutorial for iOS: Getting Started

In this Coordinator tutorial you’ll convert an iOS app from using the MVC pattern to the Coordinator pattern and examine the pros and cons of Coordinators. By Andrew Kharchyshyn.

Leave a rating/review
Download materials
Save for later
Share

The Model-View-Controller (“MVC”) design pattern is useful, but it doesn’t scale well. As projects grow in size and complexity, this limitation becomes more apparent. In this Coordinator tutorial, you’ll try a different approach: Coordinators.

If you’re unfamiliar with the term, don’t worry! It’s a simple architecture that doesn’t require any third party frameworks, and it’s easy to adopt in your existing MVC projects.

By the end of this Coordinator tutorial, you’ll be able to decide which approach works best for you when building your apps.

Note: If you’re not familiar with MVC, check out Design Patterns in iOS.

Note: If you’re not familiar with MVC, check out Design Patterns in iOS.

Getting Started

To kick things off, start by downloading the materials for this tutorial (you can find a link at the top or bottom of this tutorial).

Note: The kanji data used in this Coordinator tutorial was provided by Kanji Alive Public API

Note: The kanji data used in this Coordinator tutorial was provided by Kanji Alive Public API

Kanji List is an app for learning Kanji (Chinese characters used in the Japanese language), and it’s currently using the MVC design pattern.

Let’s take a closer look at the functionality this app provides and how:

  1. KanjiListViewController.swift contains a list of the kanji.
  2. KanjiDetailViewController.swift contains information specific to the selected kanji, as well as the list of words that use that kanji.
  3. When a user selects a word from the list, the application pushes KanjiListViewController.swift and shows the list of kanji for that word.

Coordinator Tutorial application

There’s also Kanji.swift, which holds the data models; and KanjiStorage.swift, which is a shared instance. It’s used to store parsed kanji data.

Pretty straightforward, huh?

Current Implementation Problems

At this point, you may be thinking, “The app works. What’s the problem?”

That’s a great question! Let’s take a look.

Open Main.storyboard.

OK, this looks fishy. The app has a segue from KanjiListViewController to KanjiDetailViewController; and also from KanjiDetailViewController to KanjiListViewController.

The reason is because of the app’s business logic:

  • First you push KanjiDetailViewController.swift.
  • Then you push KanjiListViewController.swift.

If you think the problem is in the segues, you’re partially right.

Segues bind two UIViewControllers together, making those UIViewControllers very difficult to reuse.

Reusability Problems

Open KanjiListViewController.swift. Notice it has a property named kanjiList:

var kanjiList: [Kanji] = KanjiStorage.sharedStorage.allKanji() {
  didSet {
    kanjiListTableView?.reloadData()
  }
}

kanjiList is a datasource for KanjiListViewController, and its default is to display all of the kanji from the shared KanjiStorage.

There’s also a property named word. This property is a hack that allows you to reuse KanjiListViewController for both the first and third screens:

var word: String? {
  didSet {
    guard let word = word else {
      return
    }
    kanjiList = KanjiStorage.sharedStorage.kanjiForWord(word)
    title = word
  }
}

By setting word, you change both kanjiList and the title that’s displayed in the UINavigationBar:

Consider for a moment. KanjiListViewController knows the following things:

  • There might be a word selected.
  • The word has kanji.
  • If there is no word selected, then the app should show all of the kanji in kanjiList.
  • KanjiListViewController knows that it’s in a UINavigationController, and that it should change the title if a word is selected.

That seems pretty complicated for something as simple as a UIViewController displaying a list of items.

One way to fix this unnecessary complexity is to pass the list of kanji to the KanjiListViewController; but who should pass it?

You can create a UINavigationController subclass and inject the data into its child, but does this code belong in the UINavigationController‘s subclass? Shouldn’t UINavigationController be a simple container for UIViewControllers?

Another problematic place is prepare(for:sender:), which is located in KanjiDetailViewController.swift:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
  super.prepare(for: segue, sender: sender)

  guard let listViewController = segue.destination as? KanjiListViewController else {
    return
  }

  listViewController.shouldOpenDetailsOnCellSelection = false
  listViewController.word = sender as? String
}

This code means that KanjiDetailViewController knows about the next UIViewController, which in this case is KanjiListViewController.

If you were to reuse KanjiDetailViewController, the code would quickly get out of hand, because this method would need to grow into a giant switch statement so it knew which view controller to push next. This is why the logic shouldn’t be inside of another view controller — it creates a strong connection between view controllers, making them have more responsibility than they should.

Coordinator Pattern

The Coordinator pattern is a potential solution to all of the problems mentioned above. This pattern was first introduced to the iOS community by Soroush Khanlou (@khanlou) in his blog and during his presentation at the NSSpain conference.

The idea of the Coordinator pattern is to create a separate entity — a Coordinator — which is responsible for the application’s flow. The Coordinator encapsulates a part of the application. The Coordinator knows nothing of its parent Coordinator, but it can start its child Coordinators.

Coordinators create, present and dismiss UIViewControllers while keeping the UIViewControllers separate and independent. Similar to how UIViewControllers manage UIViews, Coordinators manage UIViewControllers.

Coordinator Protocol

It’s time to dive into some coding!

First, create a Coordinator protocol. From the main menu, click File\New\File…. Then, select the iOS\Source\Swift File and name the new file Coordinator.swift. When you’re done, click Next and then Create.

Now, replace its contents with the following:

protocol Coordinator {
  func start()
}

Believe it or not, that’s it! All the Coordinator protocol needs is one start() function!

Apply the Coordinator Pattern

Because you want the Coordinator to handle the application’s flow, you need to provide a way, within the code, to create UIViewControllers. For this Coordinator tutorial, you’ll use .xib files instead of storyboards. With these files, you’ll be able to create UIViewControllers by calling the following:

UIViewController(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)

Note: The Coordinator pattern doesn’t require that you use .xib files. You can create UIViewControllers in code, and you can also instantiate them from storyboards, which you’ll see at the end of the Coordinator tutorial.

Note: The Coordinator pattern doesn’t require that you use .xib files. You can create UIViewControllers in code, and you can also instantiate them from storyboards, which you’ll see at the end of the Coordinator tutorial.

Add these .xib files to the project target by dragging and dropping them into the Project navigator in Xcode. Open KanjiDetailViewController.xib and KanjiDetailViewController.swift in the Assistant editor and make sure that all of the outlet connections are properly set. Do the same for KanjiListViewController.xib and KanjiListViewController.swift.

Note: In the File inspector, make sure that both .xib files are added to the target.

Adding target membership to a file in Xcode

Note: In the File inspector, make sure that both .xib files are added to the target.

Adding target membership to a file in Xcode

You need to replace Main.storyboard as the application starting point.

Right-click Main.storyboard, choose Delete then click Move to Trash.

Click on the KanjiList project in the File navigator and open the KanjiList target > General, and delete Main as the Main interface:

Removing main interface from project

Now you need to create your own starting point.