Skip to content
This repository has been archived by the owner on May 24, 2024. It is now read-only.

Navigation #10

Closed
jverkoey opened this issue Jun 15, 2011 · 35 comments
Closed

Navigation #10

jverkoey opened this issue Jun 15, 2011 · 35 comments
Assignees
Milestone

Comments

@jverkoey
Copy link
Owner

Nimbus Navigation Design Document

Version 1.0 - Last updated September 1st, 2011 at 10:29PM EST.

The goal with Nimbus navigation is to provide a powerful navigation feature that complements UIKit's existing navigation functionality. This includes iOS 5's Storyboards as well as pre-iOS 5 standard UINavigationController and modal controller presentation.

Design Considerations

This feature will complement the existing UIKit functionality by providing a thin, optional layer on top of UIKit. The goal is to allow a developer to use the navigator when they want to, and to use their own navigation logic when they don't. This means that we do not want deep integration with UIKit components. For example, we won't provide any UIViewController subclasses that implement navigation functionality like Three20 does. A developer should be completely able to slowly integrate the navigator into an existing project if they so desire. Imagine defining a single route and using it within an app that is otherwise completely custom navigation.

The Routing Interfaces

Nimbus Navigation will model directly off of Three20's navigation system by using URLs to map to view controllers and mapping objects to URLs. The routing functionality itself will be implemented using SOCKit.

The routing interface will look roughly like the following:

/**
 * A routing map maintains a mapping of routes from paths to view controllers and objects to paths.
 *
 * All routes are processed using SOCKit to gather parameter information from the paths. Each
 * navigator object (TODO: Name of the object here) instance has an instance of a NIRoutingMap.
 */
@interface NIRoutingMap : NSObject

#pragma mark Controller Routing

/**
 * Add a route from the given path to the given view controller.
 *
 * ## What happens when the navigator opens a path mapped with this method:
 *
 * A view controller will be created and initialized with the initWithNibName:bundle: method.
 * If willNavigateWithPatternParameters:queryParameters: is implemented on the controller then
 * it will be called.
 *
 * Any parameters matched in the path pattern will be passed to
 * willNavigateWithPatternParameters:queryParameters: on the view controller.
 *
 * If you require more explicit access to parameters then you may wish to consider using
 * fromPath:toViewController:withWillNavigateSelector: instead. Using an explicit selector is a
 * recommended means of using the compiler to enforce parameter types and type safety.
 *
 *      @param path  A path that may specify certain parameters that will be matched and sent to
 *                   willNavigateWithPatternParameters:queryParameters:. See the routing parameters
 *                   section of the navigation documentation (TODO: Add link here).
 *      @param viewController  The view controller that will be instantiated and presented when the
 *                             path is opened in the navigator.
 */
- (void)fromPath:(NSString *)path toViewController:(Class)viewController;

/**
 * Add a route from the given path to the given view controller.
 *
 * ## What happens when the navigator opens a path mapped with this method:
 *
 * A view controller will be created and initialized with the initWithNibName:bundle: method.
 * If willNavigateWithPatternParameters:queryParameters: is implemented on the controller then
 * it will be called. The willNavigateSelector will then be called.
 *
 * Any parameters matched in the path pattern will be passed to
 * willNavigateWithPatternParameters:queryParameters: on the view controller.
 *
 *      @param path  A path that may specify certain parameters that will be matched and sent to
 *                   willNavigateWithPatternParameters:queryParameters:. See the routing parameters
 *                   section of the navigation documentation (TODO: Add link here).
 *      @param viewController  The view controller that will be instantiated and presented when the
 *                             path is opened in the navigator.
 *      @param willNavigateSelector  A selector that should have at least as many arguments as
 *                                   there are parameters in the path pattern. One additional
 *                                   argument may provided which accepts an NSDictionary of
 *                                   NSArrays of query parameter values.
 */
- (void)fromPath:(NSString *)path toViewController:(Class)viewController withWillNavigateSelector:(SEL)willNavigateSelector;

/**
 * Returns a newly allocated controller that has been prepared for navigation using the
 * NIRoutingDestination protocol and willNavigateSelector, if such a selector was provided, with
 * the given path to provide the parameters.
 *
 *      @param path  A path that is used to 1. determine which controller class to instantiate and
 *                   2. provide the parameters to the NIRoutingDestination protocol and
 *                   willNavigateSelector, if such a selector is provided.
 *      @returns A newly allocated controller that has been prepared for navigation using the
 *               NIRoutingDestination protocol and willNavigateSelector, if such a selector
 *               was provided, with the given path to provide the parameters.
 */
- (id)controllerForPath:(NSString *)path;

#pragma mark Object Routing

/**
 * Add a route from an object class to a pattern string.
 *
 * This route is used by pathForObject: to generate a path for consumption by the navigator.
 *
 *      @param objectClass  The class of object that can be used to generate the given pattern
 *                          string.
 *      @param patternString  A pattern string that contains parameters that match property names
 *                            on the given object.
 */
- (void)fromObjectClass:(Class)objectClass toPatternString:(NSString *)patternString;

/**
 * Returns a path generated using the object's properties if a route exists for the given object's
 * class, nil otherwise.
 *
 *      @param object  The object whose values will be used by the pattern to generate the path.
 *      @returns A path generated using the object's properties if a route exists for the
 *               given object's class, nil otherwise.
 */
- (NSString *)pathForObject:(id)object;

@end

/**
 * A set of methods that can optionally be implemented by a routing destination.
 */
@protocol NIRoutingDestination <NSObject>

@optional

/**
 * Called immediately after the view controller is initialized by the navigator and before it
 * is presented.
 *
 * If a selector is provided with the routing map then this method will be called first and the
 * routing map's selector second.
 *
 * The parameters for this method are dictionaries of arrays of values. Even parameters that
 * only exist once in the pattern or query will be arrays of values. This is so that if any
 * parameter names are duplicated we provide a consistent means of accessing these values. The
 * parameter values will stored in the array in the order that they were defined in the path.
 *
 * jverkoey implementation note: Do not prepare these parameter dictionaries unless we need to.
 *      If it turns out that this is the only method that will use such a representation of
 *      parameters then we should only create them if the destination implements this method.
 *
 * jverkoey design consideration: Three20's url mapping technology forced you to define the
 *      selector for a mapped url in the url path and encouraged the use of initializers to
 *      initialize the view controller. For example, one might define a path in Three20 like so:
 *          @"fb://profile/(initWithUserId:)" => [FBProfileController class].
 *      Nimbus will not be going this route. Instead, Nimbus will encourage the use of auxiliary
 *      methods to receive the path parameters. This will allow devs to use standard controller
 *      initializers. This will have the net effect of not making it feel like Nimbus is taking
 *      over your app, instead allowing devs to graciously add functionality to existing
 *      controllers as they see fit.
 *      In Nimbus the Three20 example used above would look more like this:
 *          @"fb://profile/:userid" => [FBProfileController class] @selector(setUserId:)
 *      A multi-parameter url could look like this:
 *          @"fb://profile/:userid/:initialTab" => [FBProfileController class]
 *                                                 @selector(setUserId:initialTab:)
 *
 *      @param patternParameters  { "pattern parameter name" => NSArray of parameter values }
 *      @param queryParameters    { "query parameter name" => NSArray of query values }
 */
- (void)willNavigateWithPatternParameters:(NSDictionary *)patternParameters queryParameters:(NSDictionary *)queryParameters;

@end
@ghost ghost assigned jverkoey Jun 15, 2011
@blakewatters
Copy link

I will comment in more detail on the SOCKIt implementation (which I assume supercedes the proposal here), but I would strongly advise against using the term "object mapping" to describe string <-> object addressing. In RestKit land, object mapping refers to a conversion between KVC compliant representations of the same data. I think that this is more appropriately termed "Routing" as its aligned with Rails and RestKit's terminology.

@jverkoey
Copy link
Owner Author

Ah yes, I wrote this spec as the original design doc for SOCKit and then ran with it over the weekend. I will have to update this doc now with SOCKit in mind. The next step in designing the navigator is, as you said, building the router.

@0xdevalias
Copy link
Contributor

Ah, didn't see this one before. All sounds pretty good from my brief scan over it. I particularly like the choice to use aux methods for the 'variables' in URLs

@"fb://profile/:userid/:initialTab" => [FBProfileController class]

  •                                             @ selector(setUserId:initialTab:)
    

With the 'selectors', am I right in assuming that to implement these, FBProfileController would just have 2 public methods, setUserId and initialTab?

@jverkoey
Copy link
Owner Author

jverkoey commented Sep 2, 2011

@selector(setUserId:initialTab:) declares that a selector with the following prototype should be called:

- (void)setUserId:(NSInteger)userId initialTab:(NSString *)initialTab;

The argument types can be whatever you want and SOCKit will cast the type accordingly.

@jverkoey
Copy link
Owner Author

jverkoey commented Sep 2, 2011

For reference:

https://github.com/jverkoey/sockit

@dwery
Copy link

dwery commented Sep 2, 2011

Nice, should be much easier than Three20 and less painful to implement. Where's the Like button? :D

@jverkoey
Copy link
Owner Author

jverkoey commented Sep 3, 2011

:D It will certainly be less painful to implement. SOCKit alone kills about 10-12 different files from Three20's pattern matching implementation.

@jverkoey
Copy link
Owner Author

jverkoey commented Sep 5, 2011

More to come soon. I have a rough draft of the NINavigator object sitting on my laptop right now that still needs some love.

@thonglinhma
Copy link

I'm waiting for NINavigator :)

@0xdevalias
Copy link
Contributor

@thonglinhma

I'm waiting for NINavigator :)

I'm definitely looking forward to it as well. I keep telling myself I will get started on my app once this is done (whether that will actually happen or not is a different story.. :p)

@jverkoey
Copy link
Owner Author

It will definitely happen :) I'm getting into a nice routine of working on Nimbus in the mornings. Now that 0.8 has been released I'm planning to start focusing on the navigator for 0.9. I'll likely start working on the navigator full tilt next Tuesday when I get back to California from Waterloo.

@0xdevalias
Copy link
Contributor

Yay :) I'm hoping to be able to get into a routine myself, and hopefully be able to contribute more.

@djMax
Copy link

djMax commented Oct 11, 2011

How's this going? Starting a new app and trying to decide whether to start w/three20 while waiting for this or just wait and work on components rather than glue.

@jverkoey
Copy link
Owner Author

I got sidetracked on something that I think is going to be really cool. I'm hoping to announce it this week. It's called Chameleon and will be part of the Nimbus framework.

@jverkoey
Copy link
Owner Author

That being said, the navigation feature is going to be delayed until I get Chameleon v1 merged into Nimbus. I know a lot of people are really hoping for the navigation feature though so don't think I've forgotten about it :)

@0xdevalias
Copy link
Contributor

Can't wait to see what Chameleon has in store for us! :)

You say this is stalled till v1 is merged. I was thinking, if you had to pick a rough eta of when you think you might start on this, and then when you think it might be done it could help give those of us waiting on this an idea of whether it's worth holding out, or using TT in the interim? Just my 2c

@jverkoey
Copy link
Owner Author

http://blog.jeffverkoeyen.com/nimbus-chameleon

Let me know what you think :) This will be going out in 0.9.

I think I'm going to start the navigator work next week at this point. I'm going to be moving up to the city this weekend though so I'm not sure what my new routine is going to be like. Hopefully I'll still be able to keep up what I've been doing for the last three weeks hacking on Nimbus in the mornings before work and then at night when I'm not trampoline dodgeballing and the likes.

@RolfKoenders
Copy link

The chameleon feature is really cool! Looking forward to it. Also looking forward to the new Navigator with the SOCKit pattern! This is going to be great!

@bmeurer
Copy link
Contributor

bmeurer commented Oct 13, 2011

Yeah, that Chameleon thing looks really nice!

@thonglinhma
Copy link

Great! The chameleon is very cool. I can't wait to see NINavigator & Chameleon :)

@0xdevalias
Copy link
Contributor

Chameleon looks very very cool. Good work! :)

@thonglinhma
Copy link

How is it coming along?

@NukemHill
Copy link

Thanks. I knew I'd seen mention of this somewhere, but couldn't find it. Still sometimes have trouble navigating through Github's Issue management system.

I've got a bit of a messy app right now. It relies, primarily, on Three20. So I'm doing all of the navigation with TTNavigator. If I want to use NIToolbarPhotoViewController inside of this app (much preferable to TTPhoto...), then I either need to convert NIToolbar... to inherit from TTViewController, or use [viewController presentModalViewController:animated:]. Am I correct in that assumption? I can't seem to just push NIToolbar... onto the nav stack using [viewController.navigationController pushViewController:animated:]. Nothing happens.

@rogchap
Copy link
Contributor

rogchap commented Nov 2, 2011

@NukemHill You do not need to inherit from TTViewController to use TTNavigator. You only need to use TTViewController if you are wanting to storing view state. Just map a route to your NIToolbarPhotoViewController:

// AppDelegate.m
[map from:@"app://photo" toViewController:[MyNIToolbarPhotoViewController class]];

// ParentController.m
TTOpenURL(@"app://photo");

@NukemHill
Copy link

@rogchap - for whatever reason, I'd thought otherwise. I seem to remember trying this out and failing, so I guess I just assumed it couldn't be done with non-TT ViewControllers. I tried it out last night doing what you said, and it worked like a champ.

Thanks a ton for letting me know. This will definitely save me some grief in the short-term.

It's gonna be a beast converting my codebase over to Nimbus, though. It needs to happen, however, as the bloat in my apps is getting unmanageable. I've got a fundamental data structure and view management system that sits very nicely on top of CoreData. If I can get it transferred over to Nimbus piecemeal, then it'll be a huge win in the long run. And I'll have a library that is very adaptable to new CD-based projects. If I have to do it all at once, well.... :-/

@breathing
Copy link

so there is no navigation even for nimbus 1.0? You kept telling us that would happen but suddenly dropped the ball.. we have been waiting and waiting. now i have to switch back to three20. really disappointing.

@rogchap
Copy link
Contributor

rogchap commented Nov 21, 2011

@breathing

We have not reached a 1.0 release yet.

Sorry you feel disappointed, but you must remember this is an young open source project where development is done in our own free time; no one is getting paid and any "deadlines" are soft deadlines. We are working as hard as possible to maintain this project so that it can be of maximum use to developers of all abilities. All of the contributors to Nimbus also have full time jobs and other life commitments.

We are always happy to take on board feature requests and priority requests, and we hear your cry for navigation; have you looked at SOCKit?

In the meantime I urge you to get involved with the project; fork the project, add bits that you think are missing and then submit us a pull request.

@jverkoey
Copy link
Owner Author

Rog already covered all the important bits, so I'll just point out that you can use Three20's navigation feature relatively standalone (i.e. you don't have to use Three20UI to use Three20Navigator). I can't make any concrete guarantees on the release date for Nimbus' navigation feature so please plan accordingly.

@djMax
Copy link

djMax commented Nov 21, 2011

What would be great here is perhaps a couple paragraphs on how you plan on applying Sockit to nimbus, if it's changed or advanced from the stuff at the top of this message. I think with that some people could take a go at implementing what you've described.

@jverkoey
Copy link
Owner Author

I've decided to build a simpler navigation mechanism into Nimbus using NITableViewActions.

@dwery
Copy link

dwery commented Jun 20, 2012

uhm.. that will slow down migration from Three20 to NimbusKit. Let's see if SOCKit can be applied to NITableViewActions

@jverkoey
Copy link
Owner Author

That's a sensible idea. I want to avoid introducing too much complexity into Nimbus caused by the navigation mechanism, so it will likely not be as invasive as Three20's.

@dwery
Copy link

dwery commented Jun 20, 2012

Agreed. Navigation might be complex, but simplifies a lot when you need to call different parts of the app to react on notifications (I embed the internal URL in the notifications's dictionary) or want seamless deep linking within the app. Tables are often not the only source of actions.

@0xdevalias
Copy link
Contributor

Was there ever a consensus reached on the best way to implement this sort of thing/with SOCKit/etc?

@jnordberg
Copy link

+1, what's the status of this?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests