How I Refactored a (Massive) MVC iOS App? (Part 3)
By: Tadeh Alexani
Tags: Swift, Apple, Xcode, iOS, Refactoring

Refactoring App Routing

I really like how segue works on iOS and Swift, but the thing that hurts me is the amount of code I used to call prepareSegue function and also performing segues, besides that the number of static variables to represent the segue identifier. So after a little searching, I came across the Coordinator pattern which is firstly is introduced by Soroush Khanlou. What is this pattern and how it’s working? To explain it briefly I can quote from Paul Hudson, author of HackingWithSwift.com:

To solve this problem cleanly, the coordinator pattern lets us decouple our view controllers so that each one has no idea what view controller came before or comes next — or even that a chain of view controllers exists.

Instead, your app flow is controlled using a coordinator, and your view communicates only with a coordinator. If you want to have users authenticate themselves, ask the coordinator to show an authentication dialog — it can take care of figuring out what that means and presenting it appropriately.

The result is that you’ll find you can use your view controllers in any order, using them and reusing them as needed — there’s no longer a hard-coded path through your app. It doesn’t matter if five different parts of your app might trigger user authentication because they can all call the same method on their coordinator.

So, how I implemented this pattern on my own app?

As I mentioned in the first part of this series of articles, I refactored my app UI by separating the Main.storyboard view controllers and add some storyboard references to connect them to each other.

But using the coordinator pattern you can commit the segues entirely.

I recommend you to watch the Pauls Hudson’s tutorial on Coordinator pattern because I find it really helpful:

This tutorial is written for a single storyboard, so you can make it dynamic using the code below:

// https://stackoverflow.com/a/54782950 enum OurStoryboards: String{

case MainPage = "MainPage"

case Catalog = "Catalog"

case Search = "Search"

case Info = "Info"

case Cart = "Cart"

}



protocol Storyboarded {

static func instantiate(_ storyboardId: OurStoryboards) -> Self

}



extension Storyboarded where Self: UIViewController {

static func instantiate(_ storyboardId: OurStoryboards) -> Self {



let id = String(describing: self)

// load our storyboard

var storyboard = UIStoryboard()

switch storyboardId {

case .MainPage:

storyboard = UIStoryboard(name: OurStoryboards.MainPage.rawValue ,bundle: Bundle.main)

case .Catalog:

storyboard = UIStoryboard(name: OurStoryboards.Catalog.rawValue ,bundle: Bundle.main)

case .Search:

storyboard = UIStoryboard(name: OurStoryboards.Search.rawValue ,bundle: Bundle.main)

case .Info:

storyboard = UIStoryboard(name: OurStoryboards.Info.rawValue ,bundle: Bundle.main)

case .Cart:

storyboard = UIStoryboard(name: OurStoryboards.Cart.rawValue ,bundle: Bundle.main)

}

// instantiate a view controller with that identifier, and force cast as the type that was requested

return storyboard.instantiateViewController(withIdentifier: id) as! Self

}



}

Also, if you got a problem with launching your initial view controller in the App Delegate file (and see no result) I suggest you move it to the new Scene Delegate added in iOS 13 and Xcode 11 to make it work: