Skip to content
This repository has been archived by the owner on Aug 22, 2018. It is now read-only.

Improve URL -> VC Router for the ARSwitchboard #48

Closed
orta opened this issue Aug 22, 2015 · 5 comments
Closed

Improve URL -> VC Router for the ARSwitchboard #48

orta opened this issue Aug 22, 2015 · 5 comments

Comments

@orta
Copy link
Contributor

orta commented Aug 22, 2015

  • Support pods that can register their own selection of routes, so that we can say move all of fairs, or auctions into their own pod.
  • Support updating routes so we can do echo stuff
  • Returns a view controller
  • Params dict as a way of sending objects through
  • Support /x/:id routes
  • Support scoped routing?

JLRoutes does nearly everything here, but that it returns a BOOL rather than returning a VC. Could look into making a fork that does this, then see what Joel thinks.

@alexito4
Copy link

Hi @orta, let me comment about JLRoutes returning a BOOL.

As I told you on twitter we use a very similar thing at what you want and also using JLRoutes.
We have a wrapper on top of it that accepts our custom "Route" object.
The nice thing is that those objects are created with the "url" and a handlerBlock. Each module of the app can register it's own routes creating this objects and registering them. In the handlerBlock the VC and all the necessary dependencies are created, and that block accepts a params dictionary with the params that the caller wanted to pass (via URL params or a custom dictionary, it doesn't matter). Apart from that it also receives a block that is the one that gets called with the created VC, is the "completionBlock" that the caller uses to present the new screen.

In that way we have not modified the original JLRoutes, the Router only gives us VC (doesn't do anything with presenting them) and we can also use it asynchronously.

That's what we have come up with, I'm impatient to see how your approach will look like in the end ;) Cheers.

@orta
Copy link
Contributor Author

orta commented Aug 24, 2015

I see the pattern you're talking about, that's interesting because it also allows for asynchronous view controllers in a way that our current use case doesn't.

@orta
Copy link
Contributor Author

orta commented Aug 24, 2015

Alright thanks a lot @alexito4 - so I've hacked up a quick example based on my interpretation:

@import Foundation;
@import UIKit;

@interface Route : NSObject
@property (nonatomic, strong) NSString *pattern;
@property (nonatomic, copy) UIViewController * (^generator)(NSDictionary *params);
@end

@interface ARSwitchboardTwo : NSObject

+ (instancetype)sharedInstance;

- (void)registerRoute:(Route *)route;
- (void)routePath:(NSString *)path completion:(void(^)(UIViewController  *controller))completion;

@end
#import "ARSwitchboardTwo.h"
@import JLRoutes;

@interface _ARInternalRoute : NSObject
@property (nonatomic, copy) NSDictionary *params;
@property (nonatomic, strong) Route *route;
@end

@implementation _ARInternalRoute
@end

@implementation Route
@end

@interface ARSwitchboardTwo()
@property (readonly, nonatomic, strong) JLRoutes *routes;
@property (readonly, nonatomic, strong) NSMutableArray <Route *> *internalRoutes;
@property (readwrite, nonatomic, strong) _ARInternalRoute *currentRoute;
@end

@implementation ARSwitchboardTwo

+ (instancetype)sharedInstance
{
    static ARSwitchboardTwo *sharedInstance;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[ARSwitchboardTwo alloc] init];
    });

    return sharedInstance;
}

- (instancetype)init
{
    self = [super init];
    if (!self) {
        return nil;
    }

    _routes = [[JLRoutes alloc] init];
    _internalRoutes = [NSMutableArray array];

    return self;
}

- (void)registerRoute:(Route *)route
{
    [self.internalRoutes addObject:route];

    __weak typeof(self) weakSelf = self;

    [self.routes addRoute:route.pattern handler:^BOOL(NSDictionary *parameters) {
        _ARInternalRoute *current = [[_ARInternalRoute alloc] init];
        current.route = route;
        current.params = parameters;
        weakSelf.currentRoute = current;
        return YES;
    }];
}

- (void)routePath:(NSString *)path completion:(void (^)(UIViewController *))completion
{
    NSURL *url = [NSURL URLWithString:path];
    if ([self.routes routeURL:url withParameters:nil]) {
        Route *route = self.currentRoute.route;
        NSDictionary *params = self.currentRoute.params;
        UIViewController *result = route.generator(params);
        completion(result);

    } else {
        self.currentRoute = nil;
        completion(nil);
    }
}

@end

Then anything can register their own Routes;

@implementation TwoViewController

+ (void)load
{
    Route *route = [[Route alloc] init];
    route.pattern = @"/two/:id";
    route.generator = ^UIViewController * (NSDictionary *params) {
        TwoViewController *twoVC = [[TwoViewController alloc] init];
        twoVC.view.backgroundColor = [UIColor redColor];
        return twoVC;
    };

    [[ARSwitchboardTwo sharedInstance] registerRoute:route];
}

@end

and any other class can use their own completion based routing:

- (IBAction)go:(id)sender
{
    [[ARSwitchboardTwo sharedInstance] routePath:@"/two/thing" completion:^(UIViewController *controller) {
        [self.navigationController pushViewController:controller animated:YES];
    }];
}

Full demo - http://cl.ly/0U0Z0l1m1N2Q/RoutingExample.zip

@alloy
Copy link
Contributor

alloy commented Aug 31, 2015

@orta I like the result a lot 👍 I don’t really like the ‘current route’ state that’s needed because of the BOOL return value, so a fork does seem in order to me.

@orta
Copy link
Contributor Author

orta commented Feb 10, 2016

A lot of this is done now, and on my fork.

@orta orta closed this as completed Feb 10, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants