Episode #119

URL Scheme Callbacks

20 minutes
Published on May 15, 2014

This video is only available to subscribers. Get access to this video and 572 others.

In this episode I cover the x-callback-url draft standard, which is an attempt to formalize a way that applications can exchange data back & forth. Using this technique you can have an application expose functionality available to another application. We'll cover parsing URLs to extract out query parameters and how to respond to callbacks in your application.

Episode Links

Sending the initial call with callback params

NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"x-callback-wordrev://x-callback-url?x-source=SchemeDemo&x-success=%@&x-error=%@&x-cancel=%@&word=%@",
                                           @"schemedemo://success",
                                           @"schemedemo://error",
                                           @"schemedemo://cancel",
                                           [word stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
        if ([[UIApplication sharedApplication] canOpenURL:url]) {
            [[UIApplication sharedApplication] openURL:url];
        } else {
            NSLog(@"No app handles: %@", url);
        }

Next we receive the open url event on the receiving application, but we need to pull out all those params.

Constructing a dictionary of URL Parameters

There are probably libraries to make this easier, but it's fairly trivial to split a string of query parameters into a dictionary of key/value pairs.

    NSString *query = [url query];
        NSArray *paramValues = [query componentsSeparatedByString:@"&"];
        NSMutableDictionary *params = [NSMutableDictionary dictionary];
        [paramValues enumerateObjectsUsingBlock:^(NSString *paramValue, NSUInteger idx, BOOL *stop) {
            NSArray *parts = [paramValue componentsSeparatedByString:@"="];
            NSString *key = [parts firstObject];
            NSString *value = [parts lastObject];
            params[key] = value;
        }];

Calling back the originating application

        // x-callback-wordrev://x-callback-url?x-source=..&x-success=..&x-error=..&x-cancel&word=foo
        NSString *word = params[@"word"];
        word = [word stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
        NSString *reversed = [self reverseWord:word];

        NSString *urlString = [NSString stringWithFormat:@"%@?word=%@", params[@"x-success"], [reversed stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
        NSURL *callbackURL = [NSURL URLWithString:urlString];
        [[UIApplication sharedApplication] openURL:callbackURL];

Handling the callback in the originating application

We have a little code duplication on the URL parameter parsing (hence the desire for this to be in a library) but the process of extracting the result is straight-forward:

 if ([[url host] isEqualToString:@"success"]) {

        NSString *query = [url query];
        query = [query stringByReplacingOccurrencesOfString:@"?" withString:@""];
        NSArray *paramValues = [query componentsSeparatedByString:@"&"];
        NSMutableDictionary *params = [NSMutableDictionary dictionary];
        [paramValues enumerateObjectsUsingBlock:^(NSString *paramValue, NSUInteger idx, BOOL *stop) {
            NSArray *parts = [paramValue componentsSeparatedByString:@"="];
            NSString *key = [parts firstObject];
            NSString *value = [parts lastObject];
            params[key] = value;
        }];

        NSString *word = params[@"word"];
        word = [word stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
        [[NSNotificationCenter defaultCenter] postNotificationName:@"WordReversed"
                                                            object:word
                                                          userInfo:nil];

        return YES;
    }

Here we're just posting a notification with the transformed string so our view controller can pick it up.