iOS 9 Universal Links & Forgotten Passwords

Universal Links were introduced this year at WWDC '15, giving developers the power to integrate their app into even more user workflows.

For those that haven't dug into them yet: Universal Links let your app register to handle HTTP/HTTPS links for your domain. So when a user clicks a link to your web site, say from an email or link on Twitter, iOS checks to see if your app said it can handle that URL. If it can, your app will open instead of Safari.

Along with your app letting iOS know it can support specific URLs, you do also need to include a JSON file on your server giving specific app IDs permission to handle said URLs. This is Apple's way of verifying the owner of the URL endpoint is granting permission for apps to hijack links. So for example Tweetbot can't register to handle Twitter.com links, at least not without Twitter specifically granting them permission.

Third-party clients for popular services can find this annoying, no way Twitter will whitelist Tweetbot to handle their links, but there are valid security reasons as we'll see.

For your first-party iOS client, Universal Links aren't just for your brand or increasing engagement, they can also be used to greatly improve the user experience of previously annoying workflows.

Improving the password reset workflow

The traditional password reset workflow goes like this:

  • You forgot your password, so you hit the "forgot my password" button in the app
  • Type in your email and hit next
  • Check your email, tap the reset password link
  • The password reset web page opens in Safari
  • You enter your new password and save
  • Switch back to the app and log in using said new password

With universal links we can remove Safari from that process entirely. Users can now reset their password in-app, allowing the app to also automatically log them in after the reset. This is all possible while still having the security of the reset password email to confirm identity.

Huge win for the UX. Check it out in action:

How?

Surprisingly simple. You can read Apple's guide for supporting links or watch Session 509 from WWDC '15 for the full details, but really it came down to three code snippets.

On my web site I uploaded that previously mentioned JSON file to whitelist my iOS app to support the reset URL:

{ 
    "applinks": {
        "apps": [],
        "details": {
            "my.apps.bundle.id": {
                "paths":[ "/password/reset*" ]
            }
        }
    } 
}

In my iOS app I'm taking advantage of JLRoutes (you can always do the URL parsing yourself, that library not required) to manage my routing and URL parsing, so I register that in my app delegate during startup:

[[JLRoutes routesForScheme:@"https"] addRoute:@"/password/reset/:token" handler:^BOOL(NSDictionary *parameters) {
    NSString *token = parameters[@"token"];
    if (token) {
        //do my in-app routing to reset screen
        return YES;
    }
    return NO;
}];

And lastly I implement the Universal Link callback method on my app delegate to let iOS know if I handled the route request or not (this is the same method Handoff uses):

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler
{
    return [JLRoutes routeURL:userActivity.webpageURL];
}

And that's it. Now I'm able to intercept those URLs from Safari and show my app's reset form instead.

Universal Links are great because they aren't some scheme-specific link (ex slopes://...) that won't work when the app isn't installed. If the app isn't installed, or the user is on their desktop computer, the forgot password workflow still behaves like it used to.

We really have no excuse not to improve workflows like password-reset and email-verification for our users. Apple made it pretty trivial.

(Fun fact: when I made that video my dev site didn't have a reset form implemented yet, just the REST API for performing the task via my iOS app. Had I clicked the reset link in the email without the app installed I'd get a 404. While would lead to bad UX and I don't recommend it, it's amusing that one can use Universal Links to do everything in-app, without a web site at all, aside from the one JSON file.)