Poking a Hole in the Sandbox: Using URLs on iOS

Greg Pierce @agiletortoise

agiletortoise.com Tuesday, September 11, 12 Who am I?

Greg Pierce

President, Agile Tortoise

@agiletortoise http://agiletortoise.com

Indie app development, web consulting

agiletortoise.com Tuesday, September 11, 12 What do I do?

Drafts Terminology Phraseology

agiletortoise.com Tuesday, September 11, 12 What am I talking about?

Custom URLs schemes as tool for inter-app communication on iOS.

- Limited options in the sandbox.

- Consume URLs in other apps.

- Provide services with URLs.

agiletortoise.com Tuesday, September 11, 12 Inter-app whaaa?

Integrating with other apps can make your app...

- More useful.

- Offers great opportunities for cross-promotion.

agiletortoise.com Tuesday, September 11, 12 Overview

Part 1 - What? - Local and Lightweight

Part 2 - Why? - Use Cases

Part 3 - How? - Implementation

Part 4 - X-Callback-URL

Q & A

agiletortoise.com Tuesday, September 11, 12 Part 1 - What?

Local and Lightweight

agiletortoise.com Tuesday, September 11, 12 Anatomy of a URL

- URL = Uniform Resource Locator

scheme://domain:port/path? query_string#fragment_id

- “scheme” or “protocol” is registered with OS.

- registers “http” - Phone.app registers “tel” - Mail.app registers “mailto”

agiletortoise.com Tuesday, September 11, 12 Custom URLs

- iOS provides means to register “Custom URL schemes” for apps.

- Registering says you know what to do with “scheme:” URLs.

- Simple entries in app’s Info.plist.

- One app can register many custom URL schemes.

agiletortoise.com Tuesday, September 11, 12 URLs

- Lightweight Messaging between apps.

- No OAuth, Web Services, complex setup.

- Local

- No network require for locally installed apps.

- Few options!

agiletortoise.com Tuesday, September 11, 12 Custom URL structure

- Beyond the scheme and the colon, custom URL can look like anything.

scheme:*

- Not tied to HTTP (host:port/path) structure, but... - reuse tools/code that already exist to parse. - easier to document/consume.

- Must follow escaping/encoding guidelines - Can support 80k characters at least.

agiletortoise.com Tuesday, September 11, 12 Using URLs

- URLs can be triggered like any links

- Link embedded on a webpage

- Programmatically – two important methods:

[[UIApplication sharedApplication] canOpenURL:(NSURL *)url];

[[UIApplication sharedApplication] openURL:(NSURL *)url];

agiletortoise.com Tuesday, September 11, 12 What happens?

When a custom URL is opened...

- Source app is backgrounded. - Target app is launched (or foregrounded) - Target AppDelegate is notified with a URL, using...

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation

agiletortoise.com Tuesday, September 11, 12 Part 2 - Why?

Use cases

agiletortoise.com Tuesday, September 11, 12 Testing if app is installed

If you register a unique URL scheme, you give other apps a way to test if your app is installed.

This includes your own apps...great for cross- promoting apps, without annoying people who have already downloaded.

agiletortoise.com Tuesday, September 11, 12 Launching

Without any additional work, registering a URL scheme gives apps a way to launch your app.

See Launch Center Pro from App Cubby...

agiletortoise.com Tuesday, September 11, 12 Launching with Context

Have the user arrive at a specific place in your app...

Facebook: fb://profile fb://events

Tweetbot: ://post

agiletortoise.com Tuesday, September 11, 12 Launching with Content

Send some bit of string data to the target app.

Drafts drafts://x-callback-url/create?text=%@

OmniFocus omnifocus:///add?name=%@

agiletortoise.com Tuesday, September 11, 12 Executing an Action

Dial a phone number

tel:8175551234

Open this URL, Phone.app will launch and dial the number.

agiletortoise.com Tuesday, September 11, 12 Two-Way Interaction

- All these examples are one-way.

Source App > Target App

- What about 2-way?

Source App > Target App > Source App

- Callback URLs

agiletortoise.com Tuesday, September 11, 12 Instapaper > Terminology > Instapaper

terminology://x-callback-url/lookup? text=ceremony &x-source=Instapaper &x-success=instapaper://

agiletortoise.com Tuesday, September 11, 12 Instapaper > Terminology > Instapaper

Terminology does the “lookup”

x-source param provides friendly name for source

Tap and Terminology will trigger the url in the x-success param

agiletortoise.com Tuesday, September 11, 12 Two-Way with Data - Terminology Replace

terminology://x-callback-url/replace? text=Select &x-success=drafts://x-callback-url/replaceSelection &x-source=Drafts &x-cancel=drafts://

drafts://x-callback-url/replaceSelection &replaceWith=choose

drafts://

agiletortoise.com Tuesday, September 11, 12 Other Two-Way

- iZettle

- Credit Card Transactions

- Scanner Go

- Bar Code Scanning

- Authorization

- Dropbox SDK, Facebook SDK

agiletortoise.com Tuesday, September 11, 12 What not to do with URLs

- Destructive actions

myapp://deleteEverything

- If destructive, require user confirmation.

- Think about HTTP GET RESTful actions.

agiletortoise.com Tuesday, September 11, 12 Part 3 - How?

Implementation

agiletortoise.com Tuesday, September 11, 12 URLScheme Demo App

Example app on Github

https://github.com/agiletortoise/URLSchemes

agiletortoise.com Tuesday, September 11, 12 Consuming URLs

agiletortoise.com Tuesday, September 11, 12 Building URLs

#import "Scratchpad.h" #import "NSString+URLEncoding.h" // Category for encoding

@implementation Scratchpad - (void)examples { NSString *urlString = @"test://action?param1=%@¶m2=%@"; NSString *p1 = @"Hello World"; NSString *p2 = @"You’re Awesome";

NSString *fullURLString = [NSString stringWithFormat:urlString, [p1 urlEncodeUsingEncoding:NSUTF8StringEncoding], [p2 urlEncodeUsingEncoding:NSUTF8StringEncoding]]; NSURL *url = [NSURL URLWithString:fullURLString]; } @end

test://action? param1=Hello%20World ¶m2=You%E2%80%99re%20Awesome

agiletortoise.com Tuesday, September 11, 12 Opening URLs

if ([[UIApplication sharedApplication] canOpenURL:url]) { [[UIApplication sharedApplication] openURL:url]; }

- Use canOpenURL to build UI, don’t show things that can’t be used.

- canOpenURL doesn’t need full URLs, test with just “scheme://”

agiletortoise.com Tuesday, September 11, 12 Providing URLs

agiletortoise.com Tuesday, September 11, 12 Register scheme

agiletortoise.com Tuesday, September 11, 12 Register in Info.plist

agiletortoise.com Tuesday, September 11, 12 Valid URL Scheme Names

- Sequence of characters beginning with a letter and followed by any combination of letters, digits, plus ("+"), period ("."), or hyphen ("-").

- NOT case sensitive.

agiletortoise.com Tuesday, September 11, 12 Recommended URL Scheme Names

- Your app name as it appears on the device, with hyphens in place of spaces.

My App Name ⬇ my-app-name://

agiletortoise.com Tuesday, September 11, 12 Versioning

If you provide actions that you need to version over time, register additional URLs

x-app-api10:// x-app-api20://

Now you have a way to test for compatibility.

agiletortoise.com Tuesday, September 11, 12 Reacting to URL

On AppDelegate, define:

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation; c

- called after applicationWillEnterForeground - called before applicationDidBecomeActive - return NO if you don’t recognize the URL - (Nothing happens, just best practice)

agiletortoise.com Tuesday, September 11, 12 iOS 3 note...

Prior to iOS 4, you handled URLs in....

- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url;

If you need to support iOS 3, implement this method and forward to your application: openURL: sourceApplication: annotation method.

agiletortoise.com Tuesday, September 11, 12 Parsing Parameters

myapp://actionName?test=param%20value&test2=paramValue

URLParser *parser = [[URLParser alloc] initWithURLString:[url absoluteString]]; NSString *testParamValue = [parser valueForVariable:@"test"]; // testParamValue == @”param value”

agiletortoise.com Tuesday, September 11, 12 Parsing Parameters

If you parse using some other method, USE THIS METHOD TO DECODE!!!

[str stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]

agiletortoise.com Tuesday, September 11, 12 DEMO

(unreheased, apologies in advance)

agiletortoise.com Tuesday, September 11, 12 Part 4

X-Callback-URL

agiletortoise.com Tuesday, September 11, 12 X-Callback-URL

- Specification to standardize URL formats

- Drafts, Terminology, Instapaper, Agenda, Due, iCab Mobile, Poster, Phraseology, Notesy.

- Collaboration of myself, with input from a few others.

http://x-callback-url.com

agiletortoise.com Tuesday, September 11, 12 X-Callback-URL format

scheme://x-callback-url/[action]?[x-callback parameters]&[action parameters]

- [action] is specific to your app/implementation and should typically be a verb.

- [action parameters] are also specific to your app and show be in a URL query string...i.e.:

subject=The%20Subject&body=The%20Body

agiletortoise.com Tuesday, September 11, 12 X-Callback-URL Parameters

x-source: Friendly name of source app.

x-success: Base URL to fire if successful

x-error: Base URL to fire if error occurs

x-cancel: Base URL to fire if user cancels

agiletortoise.com Tuesday, September 11, 12 Finally....

- Why use URL schemes? Why not?

- Why format them with X-Callback-URL? Why not?

- Document what you implement!!!!!!

- Use affiliate links to promote apps you integrate with!!!!!

- UTF8 escape and decode parameters!

- Test with Unicode chars and cold starts.

agiletortoise.com Tuesday, September 11, 12 Resources

Find URL schemes: - http://handleopenurl.com - http://wiki.akosma.com/IPhone_URL_Schemes

Tutorial: - http://mobile.tutsplus.com/tutorials/iphone/ios-sdk-working- with-url-schemes/

X-Callback-URL: - http://x-callback-url.com (sample apps/examples)

URLSchemes Demo App: - https://github.com/agiletortoise/URLSchemes

agiletortoise.com Tuesday, September 11, 12 Q & A @agiletortoise

Slides and Links: http://agiletortoise.com/blog

agiletortoise.com Tuesday, September 11, 12