Clean Swift Router and Coordinator for declarative routing tree matching
Clone or download
agentk Merge pull request #17 from raven/master
Update framework targets to have 'ProvisionStyle: Automatic'
Latest commit ba50bc8 Oct 5, 2016
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
Demo Cleanup remaining Swift 3 migrations Oct 4, 2016
FeatherweightRouter.xcodeproj Update framework targets to have 'ProvisionStyle: Automatic' Oct 5, 2016
Sources Cleanup remaining Swift 3 migrations Oct 4, 2016
Tests Cleanup remaining Swift 3 migrations Oct 4, 2016
.gitignore
.hound.yml Remove all Foundation and UIKit dependencies Jun 13, 2016
.jazzy.json Remove all Foundation and UIKit dependencies Jun 13, 2016
.swiftlint.yml Remove all Foundation and UIKit dependencies Jun 13, 2016
.travis.yml
CHANGELOG.md Remove all Foundation and UIKit dependencies Jun 13, 2016
LICENSE Initial commit Jan 7, 2016
Package.swift Remove all Foundation and UIKit dependencies Jun 13, 2016
README.md Update README with current examples. Oct 4, 2016

README.md

Featherweight Router

Swift based Application State Router

Featherweight Router is a declarative routing handler that decouples ViewControllers from each other. It follows a Coordinator and Presenter pattern, also referred to as Flow Controllers.

Featherweight Router makes an excellent MVVM companion and fits right in with Redux style State Flow and Reactive frameworks.

The Coordinator is constructed by declaring a route hierarchy mapped with a URL structure.

By using mapping UI to URLs, it makes it easy to add automatic URL scheme handling in the future.

Principle Concepts

UI is a representation of State

As the State changes over time, so will the UI projection of that State.

Given any State value the UI must be predictable and repeatable.

Device dependent state should be separate from the Application State.

Displaying the same State on a phone and tablet for example, can result in different UIs. The device dependent state should remain on that device. An OS X and iOS app can use the same State and logic classes and interchange Routers for representing the State.

Application State encapsulate Path

If the UI is a projection of State, then the current Path should be included in that State too.

UI can generate actions to update Path values in the State

The user tapping a back button is easy to capture and generate and action that updates the State Path which causes the UI change. But a user 'swiping' back a view is harder to capture. It should instead generate an action on completion to update the State Path. Then, if the current UI already matches the new State no UI changes are necessary.

All View components are predictable projections of State

Each view component should be testable and predictable. If any component that makes up the UI is not predictable, then neither is the UI.

Actual State values should be passed into presenters outside of the Routers

Although the UI should be a projection of State + Path only the Path should be passed to the Router when State changes. Using the Router for State propagation ties the Router to the State. Following the single responsibility principle, the Router only needs updates when the Path changes and callbacks to notify State of Path changes caused by the user outside of the standard Routes (ie, user swiped back in a navigation controller).

Goal Usage

import FeatherweightRouter

func appRouter(store: AppStore) -> Router<UIViewController, String> {

    return Router(tabBarPresenter()).junction([

        Router(navigationPresenter(title: "Animals")).stack([
            Router(animalListPresenter(store)).route(predicate: {$0 == "animals"}, children: [
                Router(animalPresenter(store)).route(predicate: {$0.matches("(?<id>\\w+)")}),
            ]),
        ]),
        Router(navigationPresenter(title: "Zoos")).stack([
            Router(zooListPresenter(store)).route(predicate: {$0 == "zoos"}, children: [
                Router(zooPresenter(store)).route(predicate: {$0.matches("(?<id>\\w+)")}),
            ]),
        ]),
    ])
}

func appCoordinator() -> UIViewController {

    let store = createStore(reducer: appReducer, initialState: nil)
    let router = appRouter(store: store)
    store.state.map { $0.route }.subscribe(next: router.setRoute)

    return router.presenter
}

Glossary

  • Path / URL: A String representing the current view or UI state the application is in. This can include hierarchy and view dependent information as parameters or query values. Ie, viewing a user profile
  • State: A stream of values over time.
  • UI: User Interface: A visual representation of the State that a user can interact with.

License

All content is licensed under the Apache-2.0 unless otherwise stated.

Copyright is owned by Karl Bowden and Featherweight Labs unless otherwise stated.