Matthew's Dev Blog

Data-driven Screen Dispatching for iOS

The Coordinator Pattern is rather trendy at the moment. Soroush Khanlou and Paul Hudson have both written very fine blogposts about it, and I saw Paul talk about coordinators - briefly - at iOSDevUK in 2018. There are some great benefits from using Coordinators, but I can't help feeling it doesn't go far enough.

I propose that app navigation should be driven by data.

Data-driven navigation in a nutshell

My app doesn't want to show ViewControllers to the user; it wants to show data.

Architectural background for my apps

  • I generally use a variation of MVVM, so user-interface state is held outside the ViewController
  • this means my ViewControllers are always small and free of logic

Consequently, I really don't care about ViewControllers. I don't want to create them, I don't want to present them, I don't want to write code to wire them up.

Data-driven navigation example

In my Nearly Departed rewrite, I have a RouteModel which defines a pair of rail stations:

struct RouteModel {
	let fromStationCode: String
	let toStationCode: String
}

My ViewModels will have a reference to a dispatcher object (more on that later), and will present a route like this:

let yorkToLeeds = RouteModel(fromStationCode: "YRK", toStationCode: "LDS")
dispatcher.dispatch(yorkToLeeds)

That's it. We're showing data to the user, not a ViewController.

The RouteModel can be dispatched because of an extension which makes it conform to a Dispatchable protocol - and it's that extension which decides how the data is shown to the user:

extension RouteModel: Dispatchable {
	var dispatch: DispatchChange {
		let viewModel = DeparturesViewModel(route: self)
		return DispatchChange(presentation: .modal,
							  options: .embedInNavigationController,
							  viewModel: viewModel,
							  viewControllerType: DeparturesViewController.self)
	}
}

Why?

There are several nice outcomes of this:

  • my ViewModels can use a dispatcher to show data to the user; they don't care about how it's shown
  • it's easily unit-testable; I can use SwiftMock to create a mock for the Dispatching protocol, and easily assert that the correct data is being presented
  • it still has the benefits of Soroush's coordinators - where ViewControllers are completely decoupled from each other

Source

There's an example implementation in this Gist.

Tagged with:

First published 26 January 2019