Is there a way to register a url with query params in Three20?

456 views Asked by At

I have a url with a varying number of query params:

myapp://profile?username=1&status=2&title=3

I would like to register something like this with TTUrlMap

[map from:@"myapp://profile*" toViewController:[ProfileController class]];

And I would like Three20 to recognize the "rest of the url" and either invoke something like:

initWithOriginalUrl:(NSString*) originalUrl

where I can then parse the query params or:

initWithQueryParams(NSDictionary*) queryParams 

where TTNavigator has recognized my url, parsed the params into a map and then invoked my controller passing the query params?

Is this supported? I would rather not pass the encoded url as a param as was suggested here: Pass URL Question

2

There are 2 answers

0
Patrick Cullen On BEST ANSWER

I'm going to answer this myself - the trick is knowing about initWithNavigatorURL - Three20 will parse your query params, call this method and pass the parsed params to it if you haven't explicitly set a method in your map. So the solution is to add this to your map:

[map from:@"myapp://profile" toViewController:[ProfileController class]];

and implement the magic initWithNavigatorUrl method on your toViewController

@implementation ProfileController

- (id)initWithNavigatorURL:(NSURL*)URL query:(NSDictionary*)query {
    NSLog(@"ProfileController initWithNavigatorUrl %@, %@", URL, query);  
    ....
1
Mike Morearty On

Yes, there's a way to do this. If the target class of the URL is, for example, ProfileController, then register the URL like this:

[map from:@"myapp://profile?originalURL=(initWithOriginalURL:)"
  toViewController:[ProfileController class]];

As you can see, the value of the query parameter named originalURL will be passed as the first argument to a function called initWithOriginalURL:. So in ProfilerController, declare that function:

- (id)initWithOriginalURL:(NSString*)originalURL {
    // Initialize your controller.  For example, you might do this:
    if (self = [self initWithNibName:nil bundle:nil]) {
        self.variableHeightRows = YES;

        self.dataSource =
            [TTListDataSource dataSourceWithObjects:
             [TTTableLongTextItem itemWithText:[NSString stringWithFormat:@"Original URL is %@", originalURL]],
             nil];
    }

    return self;
}

So the URLs you can open will look like myapp://profile?originalURL=URL_GOES_HERE. Note that just as with URLs on the Internet, it is important to URL-encode any and all query parameters. So here is an example of code that will open the above ProfileController:

// any URL goes here -- this is the query parameter we are going to
// pass as the "originalURL=..." parameter.
NSString* url = @"http://www.google.com/search?hl=en&q=stack+overflow";

// URL-encode it: turn most non-alphanumerics into %XX
NSString * encodedURL = (NSString *)CFURLCreateStringByAddingPercentEscapes(
    NULL,
    (CFStringRef)url,
    NULL,
    (CFStringRef)@"!*'\"();:@&=+$,/?%#[] ",
    kCFStringEncodingUTF8 );

// Open the URL
TTOpenURL([NSString stringWithFormat:@"myapp://profile?originalURL=%@", encodedURL]);

In this case, encodedURL will end up being:

myapp://profile?originalURL=http%3A%2F%2Fwww.google.com%2Fsearch%3Fhl%3Den%26q%3Dstack%2Boverflow