Skip to content

Instantly share code, notes, and snippets.

@moudy
Created April 2, 2015 21:23
Show Gist options
  • Save moudy/ee3d446ffcaba370522c to your computer and use it in GitHub Desktop.
Save moudy/ee3d446ffcaba370522c to your computer and use it in GitHub Desktop.

iOS Router Thoughts

We don't have a clear, flexible solution for transitioning between screens. These are some thoughs of what we need.

Transtion to any screen, from any screen

For example, a username can appear on various screens. Tapping the username results in transtioning to the UserDetail screen ragardless of what the current screen.

// This should be possible from any RouteContext
this.router.push(.UserDetail, model: user)

Decouple RouteContext from transtion style

It should be possible to transition to any route with any transtion style.

// These each show the same screen, with different transtions
this.router.push(.UserDetail, model: user)
this.router.present(.UserDetail, model: user)
this.router.transitionTo(.UserDetail, model: user, style: customTranstion)

Lifecycle hooks for RouteContext

A route should know when it's being entered/exited/shown/hidden.

protocol RouteContext {
  func enter()
  func exit()
  func willShow()
  func willHide()
}

class UserDetailRoute: RouteContext {
  func enter() {
    self.fetchInitialData()
  }

  func exit() {
    this.cleanup()
  }

  func willShow() {
    this.startAnimations()
  }

  func willHide() {
    this.pauseAnimations()
  }
}

Thoughts on how to implement

// Routes.swift -- global routes, not scoped to router
enum Route {
  case UserDetail
  case FileInstanceDetail
  case Login
  // etc.
}

// Routable.swift
protocol Routable {
  public func push(route: Route, model: AnyObject)
  public func present(route: Route, model: AnyObject)
  public func transitionTo(route: Route, model: AnyObject, style: TransitionDelegate)
}

// AppRouter.swift
class AppRouter {
  let rootViewController = UINavigationController()

  public func push(route: Route, model: AnyObject? = nil) {
    let vc: UIViewController
    switch route {
      case .UserDetail:
      if let user = model as User {
        let context = UserDetailRoute(user: user)
        vc = UserDetailViewController(context: context)
        context.enter()
      }
    }
    navigationController.pushViewController(vc, animated: true)
  }
}
@yusefnapora
Copy link

This seems a lot cleaner and more reusable than what we have now.

Just thinking out loud, but what if the RouteContext included a type alias for the model:

protocol RouteContext {
  typealias Model
  func enter(model: Model)
  func exit()
  func willShow()
  func willHide()
}

Then Router.push could be something like:

public func push<T>(route: Route, model: T) {
    let vc: UIViewController
    switch route {
      case .UserDetail:
        let context = UserDetailRoute()
        vc = UserDetailViewController(context: context)
        context.enter(model)
      }
    }
    navigationController.pushViewController(vc, animated: true)
  }

Not sure if that makes sense or not, and it makes 'model' a required param of push. Some routes might not need a model param, so maybe that doesn't really work…

I like where you're going with this; I think we need to do something to make the router more flexible. I'll think some more and hopefully have more to say soon :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment