One of the biggest problems with the big view controllers is that they entangle your flow logic, view logic, and business logic.

When a table cell is selected, that delegate method typically looks like this:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {  
	id object = [self.dataSource objectAtIndexPath:indexPath];  
	SKDetailViewController *detailViewController = [[SKDetailViewController alloc] initWithDetailObject:object]  
	[self.navigationController presentViewController:detailViewController animated:YES];  
}  

Three simple lines: get the object; create a view controller; present the view controller. In a simple app, this works great. Each view controller is probably used once, and in only one context. Coupling them together like this isn’t a dangerous proposition. As your app grows in complexity, however, the same view controller might get used in a new way or in a new place. Maybe it’s on the iPad. Maybe it’s in an extension. Maybe it’s a new flow for doing an old thing.

If the flow logic is encoded into the view controller itself, the view controller can’t be reused for its stunningly good looks without dragging all that flow code with it. Don’t forget, the view controller base class is prefixed with UI. It’s view object, and handling user flow is out of its scope.

In the code example above, the view controller knows how to create the next thing in the flow, and how to present it. In the third line of code, it tells its parent view controller what to do, which definitely seems backwards. And even worse, that flow code is distributed among multiple view controllers, each of which only knows how to perform the next step.

I used to think of the view controllers as the highest level thing in app, the things that know how to run the whole show. Lately, however, I’ve found many advantages to having the view controllers be bossed around by an even higher level object, one whose role is to marshal and manage all the view controllers in its purview.

I call these objects Coordinators, but I’ve also heard them called Directors. To really execute this pattern well, you need one high-level coordinator that directs the whole app (this is sometimes known as the Application Controller pattern). The app delegate holds on to the AppCoordinator. Every coordinator holds an array of its child coordinators. Especially if you have more than one, as in a tab bar app, each navigation controller gets its own coordinator, for directing its behavior and flow. Further child coordinators can be created for specific tasks like signing up or creating content. Each coordinator is spawned by its parent coordinator. As always, use this pattern early in the development process, so they’re useful even in single-step tasks, such as authentication.

The Coordinator is a PONSO, like all great objects. For something like Instagram’s photo creation flow, we could have a PhotoCreationCoordinator. The app coordinator could spawn a new one, and pass it the root view controller so that it could present the first view controller in the flow.

- (void)beginPhotoCreationProcess {  
	PhotoCreationCoordinator *coordinator = [[PhotoCreationCoordinator alloc] initWithRootViewController:self.rootViewController delegate:self]  
	[self.childCoordinators addObject:coordinator];  
	[coordinator beginPhotoCreationProcess];  
}
	
- (void)photoCreationCompletedSuccessfully:(PhotoCreationCoordinator *)coordinator {  
	[self.childCoordinators removeObject:sessionCoordinator];  
}
	
- (void)photoCreationCanceled:(PhotoCreationCoordinator *)coordinator {  
	[self.childCoordinators removeObject:sessionCoordinator];  
}  

The coordinator’s -beginPhotoCreationProcess method can now create a new photo selection view controller and configure it as needed. The coordinator can conform to the photo selection view controller’s delegate so that it can be informed when it’s time to present the next step. None of the view controllers in the process need to know about any of the other view controllers. Each view controller is an island.

Business logic, like posting the photo, is wrapped up in its own object and can either be pushed up to the coordinator or pushed down to the model as appropriate. Either way, it comes out of the view controller. Moving it up is great because the coordinator is already acting as the glue between different parts of your code already.

The benefits to extracting the flow into a coordinator are myriad. View controllers now can focus on their goal of binding a model to a view. The can be reused more easily, such as in extensions or unbundled apps, without having complex conditionals to manage different contexts. A/B testing is as simple as creating a different coordinator object and using that to start the process. Flow logic now has a home, and things that have a home feel real nice.

View controller initialization is also extracted. This shouldn’t be overlooked. Initializing is always a more complex task than it seems, requiring lots of knowledge about classes and configuations, and we moved it to a better place where more informed decisions can be made.

Coordinator objects naturally fall into one of many disjoint states — in other words, especially for an iPhone app, only one screen is visible at any time. This makes them a ripe place to use state machines to manage all of the data in their custody.

Apple loves having the view controller be the center of the iOS world. This is obvious in the new UISplitViewController APIs in iOS 8, as well as Interface Builder components like storyboard segues. Unfortunately, a view controller-centric approach to app development isn’t scalable. The Coordinator is a great way to isolate your code into small, easily-replaced chunks, and another part of the solution to view controller malaise.