Skip to content
This repository has been archived by the owner on Feb 5, 2020. It is now read-only.

Weavy in details

Thibault Wittemberg edited this page Nov 20, 2017 · 9 revisions

Navigation concerns

Regarding navigation within an iOS application, two choices are available:

  • Use the builtin mechanism provided by Apple and Xcode: storyboard and segues
  • Implement a custom mechanism directly in the code

The disadvantage of these two solutions:

  • Builtin mechanism: navigation is relatively static and the storyboards are massive. The navigation code pollutes the UIViewControllers
  • Custom mechanism: code can be difficult to set up and can be complex depending on the chosen design pattern (Router, Coordinator)

Weavy aims to

  • Promote the cutting of storyboards into atomic units to enable collaboration and reusability of UIViewControllers
  • Allow the presentation of a UIViewController in different ways according to the navigation context
  • Ease the implementation of dependency injection
  • Remove any navigation mechanism from UIViewControllers
  • Promote reactive programing
  • Express the navigation in a declarative way while addressing the majority of the navigation cases
  • Facilitate the cutting of an application into logical blocks of navigation

The weaving analogy

According to Wikipedia

Weaving involves using a loom to interlace two sets of threads at right angles to each other: the warp which runs longitudinally and the weft that crosses it [...] The method in which these threads are inter-woven affects the characteristics of the cloth. Cloth is usually woven on a loom, a device that holds the warp threads in place while filling wefts are woven through them.

What the heck does this have to do with navigation ?

As my experience grew as an iOS developper (as well as an Android or a Web app developper), I constantly faced the same doubts regarding navigation. For all other conception issues, there were plenty of patterns to address common architecture questions and separation of concerns needs (MVC, MVP, MVVM, VIPER, Dependency Injection, Facade, Protocol Oriented Programing, Reactive programing, ...).

But I was torn appart as soon as navigation was to be designed:

  • How do I use dependency injection with Storyboards/Segues ?
  • How do I control the flow of the application ?
  • How do I get rid of the navigation boilerplate code from the ViewControllers ?

As time went by, my conception of an iOS application went from MVC with one Storyboard, to MVC with multiple Storyboards, to finally reach what we could name one of the nowadays best practices: MVVM with Flow coordinator. Which is perfectly fine because we can play with dependency injection, ViewControllers reusibility, testability. I had the chance to apply this pattern to huge and complex applications in production. But in the end, there were still a couple of issues that bothered me:

  • I always had to write the Coordinator pattern, again and again,
  • there were a lot of delegation patterns used to allow ViewModels to communicate back with Coordinators.

I began to look at the Redux pattern, especially the navigation state mechanism. We could have a global navigation state, exposed with a RxSwift Observable, and something listening to this state and driving the navigation. The only thing that I found disturbing was the uniqueness of this navigation state, and the uncontroled responsabilities it could have (as well as the massive data it could store)

The idea that the navigation was only the reflexion of a state that could be modified step by step begun to emerge. A state that would be spread within the whole application structure, not stored in a single place, but unified by an observer that could react to it and drive the navigation as a consequence. Later in this Readme, these little states spreaded in the application are called the Wefts and the observer is called the Loom. We begin to make the link between navigation and weaving: Navigating is like inserting some presentation code bit by bit as the user plays with the application and modify all these little navigation states. In the end, navigation forms a pattern, pretty much the same idea than when a real loom weaves some fabric with wool. This pattern is unique and represent the usage of the application from opening to ending. For those who are familiar with Aspect Oriented Programming, I find the idea of "navigation weaving" pretty close to what we can find in AOP. The navigation actions would be some advices injected in specific join-points through a weaving process.

Weavy is born from all that experience and addressed the two mains concerns that remained from the Coordinator pattern:

  • the developper must no more write Coordinators, he only has to declare the navigation and the states it react to,
  • delegation is not needed anymore, since states are RxSwift Observable observed by the Loom

The core principles

This is how I imagine the weaving pattern in a simple application:

How do I read this ?

  • Here we have three warps: Application, Onboarding and Settings which describe the three main navigation sections of the application.
  • We also have three wefts: Dashboard (the default weft triggered at the application bootstrap), Set the server and Login.

Each one of these wefts will be triggered either because of user actions or because of backend state changes. The crossing between a warp and a weft represented by a colored chip will be a specific navigation action (such as a UIViewController appearance).

As we can see, some wefts are used in multiple warps and their triggering will lead to the display of the same screen, but with different presentation options. Sometimes these screens will popup and sometimes they will be pushed in a navigation stack. This sketch illustrates how we can factorize UIViewControllers and Storyboards.

Warp, Weft and Stitch

Combinaisons of Warps and Wefts describe all the possible navigation patterns within your application. Each warp defines a clear navigation area (that makes your application divided in well defined parts) in which every weft represent a specific navigation action (push a VC on a stack, pop up a VC, ...).

In the end the knit function has to return an array of stitches. A Stitch helps the Loom in knowing what it will have to deal with for the next navigation steps.

Why an array of stitches ? For instance a UITabbarController is a pattern in which multiple navigations are done at the same time, and we need to tell the Loom something like that is happening.

A Stitch tells the Loom: The next thing you have to handle is this particular Presentable and this particular Weftable. In some cases, the knit function can return an empty array because we know there won't be any further navigation after the one we are doing.

The Demo application shows pretty much every possible cases.

Weftable

The basic principle of navigation is very simple: it consists of successive views transitions in response to application state changes. These changes are usually due to users interactions (such as button clicking or list picking), but they can also come from a low level layer of your application. We can imagine that a lose of network session could lead to a signin screen appearance.

The most generic way of considering all this is to imagine that anything in the application can lead to a change of navigation state. We need a way to express these changes. As we saw, a combinaison of a Warp and a Weft represent a navigation action. As well as Warps can define your application areas, Wefts can define these navigation state changes inside these areas.

Considering this, a Weftable is basically "something" in the application which is able to express a new Weft, and as a consequence a navigation state change, leading to a navigation action.

A Weft can even embed inner values (such as Ids, URLs, ...) that will be propagated to screens presented by the weaving process.

Loom

A loom is a just a tool for the developper. Once he has defined the suitable combinations of Warps and Wefts representing the navigation possibilities, the job of the loom is to weave these combinaisons into patterns according to navigation state changes induced by Weftables. It is up to the developper to:

  • define the Warps that represent in the best possible way its application sections (such as Dashboard, Onboarding, Settings, ...) in which significant navigation actions are needed
  • provide the Weftables that will trigger the Loom weaving process.

Let's get back to Weavy. We saw that combinaisons of Warps and Wefts are weaved into Stitches that represent screens to be displayed. The big advantage of doing this programmatically is that we have the opportunity to inject whatever service we want into the UIViewControllers the Loom will instantiate. The only thing we need at this point is a a mechanism that will help the developper to provide these services.