Learn how to increase app engagement by using universal links to lead users directly to your App when they tap links to your website. Discover how Smart App Banners and Shared Web Credentials give your users the most integrated mobile experience possible.
CONRAD SHULTZ: Hi, my name is Conrad Shultz
and I'm an engineer on the Safari and WebKit team
and this is Session 509, Seamless Linking to Your App.
So, if you're here at WWDC, you almost certainly have an app
and odds are you have a website for that app.
So a quick show of hands, how many of you have a website
that allows users to access some of the same content
or functionality as in your app?
Great. This talk is for you.
So we are going to talk about how we can tear down the walls
between your app and your website
and create a tight association between them.
As an example of what we are going to talk about,
I would like to begin with a demo of an app
that you probably have installed on your iPhone
or iPad right now, the WWDC app.
So here I have a message from my colleague, Jonathan,
telling me to check out a keynote.
And that sounds like a good idea,
and I have the WWDC app installed on this iPad.
And I see this is a web link
so it will probably still open in Safari.
Let's go ahead and tap on it.
And, huh, that's pretty cool, it took me right to the WWDC app.
Maybe you missed that.
Let's do that again.
I will go ahead and tap on back to messages in the upper left.
I see this is just a regular HTTPS link going
to Developer.Apple.com, and I tap on it, and, boom!
Right to the WWDC app, right to the keynote.
No ping ponging through Safari.
So we are going to talk about three cool technologies
to help you tear down those walls
between your app and your website.
First, the magic you just saw in the demo,
where the WWDC app opened to the right place
when a regular link was tapped.
Next, we are going to talk about Smart App Banners,
which allow users to discover your app
when they visit your website.
Finally, shared web credentials, which allows your app
to access Safari-saved passwords and, therefore, make logging
into your app a breeze for your users.
To start us off, I would like to invite my colleague,
Jonathan to the stage to talk to us about linking to your app.
JONATHAN GRYNSPAN: Thank you, Conrad.
Hello everyone, my name is Jonathan Grynspan
and I'm a Core Services Software Engineer here at Apple.
Today I would like to take a moment to talk to you
about your app's place in the universe.
I saw a lot of hands go up when Conrad asked how many
of you have both an app and a website.
And it's not surprising.
Many modern apps are more than just what the user downloads.
They also have a strong web presence.
Often web content mirrors app content.
Many of you have a great website with great functionality,
and we like how your users are able to reach that functionality
without even having to install an app.
We also like when your apps work together.
An app exists as part of an ecosystem.
If your app, or website, needs to access content provided
by another app, or website,
it needs to communicate with it somehow.
For example, if you have an app for foodies and you want to link
to a recipe, you might want to use an app
or service, such as Yumly.
So how does your app get to Yumly,
and how does it provide Yumly's content correctly and quickly?
On iOS we usually use URLs to send data
from one app to another.
So let's take a look at how we do that today.
Many iOS apps implement what's called a custom URL scheme.
These are a very effective tool,
because they let apps communicate with each other
but they do have a few pitfalls.
First off, they don't always map to the right app,
because there is no app that truly owns them.
Two apps can claim the same URL scheme
and users don't really have a good way
to say which app they mean.
Second off, if your app is not installed, they fall flat.
The URL doesn't open and the app needs custom code in order
to handle this fairly common situation.
And finally, they make protecting user's
Apps need to be able to detect if other apps are installed
on a device, in order to use custom URL schemes.
This means that they can sniff out if a user has a dating app,
or a banking app,
or a politically-charged app installed.
This is personal information,
and users do not want to share it.
So, there has to be a better way, but what does
that better way look like?
Well, obviously, it needs to be just as easy to use
as custom URL schemes, so we are still talking
about a kind of URL.
It must have verifiable ownership,
URLs should always open in the right app.
There should be a strong two-way association
between your app and your URLs.
It must have the ability to fall back gracefully,
if your app is not installed.
The URL should still work when a user taps on it.
Safari, and web browsers in general, are a great way
to display rich content, and are present
on nearly every platform, so we'll make sure
that these links can open in a web browser.
And it absolutely must protect a user's privacy.
When I run an app on my device, it has no right to know
that I also have your app installed.
And, of course, the links that, the --
excuse me, and, of course, the links that you tap are
between you and your device.
There is no need for a third party to be involved.
As Conrad showed you, web URLs are the ideal solution
that fit all of these criteria.
You know when they are going to open,
they work nearly everywhere, and no third party needs
to be involved when you tap one.
Now that we have identified web URLs
as our solution, let's break one down.
I know you have all seen a URL before, but if we take a look
at the components of a URL, we'll have a better idea
of how these links are going to work.
Here is a perfectly normal web URL.
If you are curious, this links
to last year's WWDC keynote video.
This is not a new URL that we had to generate just
for this purpose, or just for this app.
It already existed, and you can visit it
on the Apple Developer Connection website today.
If I open it in Safari, I expect to see exactly the content
that you would expect, the video and information about it.
If I - uh, I also expect to see the same content if I open this
on a Mac, or on another platform entirely, they are universal.
And thanks to universal links, this link will open
in our WWDC app without a web browser getting involved.
So, in order for that to work,
iOS needs to look for a few things.
The first thing we look at is the scheme.
We support HTTPS and HTTP URLs.
Next up is the domain, or host name.
The domain is securely associated with your app,
by using an SSL certificate to sign a file that is stored
on your secure web server.
I will have more info on that in just a moment.
After we have analyzed the scheme and the domain,
we take a look at the path.
The path can either match exactly,
or it can match with a prefix.
In both cases, you specify the matches, uh, sorry,
you specify the matches and you are in control of the situation.
This is useful if you have content that is on your website
but is not representable in your app yet.
If a URL does not match, it opens in Safari just
like any other link would.
If we do match a URL and we need to - sorry, if we do match a URL
and send it to you, you can use NS URL components
to break it down, and to get at the information
in the URL that's useful to you.
I particularly like using NS URL Query Item
because it makes accessing the components
of the query super simple.
Universal links mean having your app, not Safari, handle links
to your website, but in order to get-- in order to open your app,
when a user taps a link to your website, iOS needs to know
that your app should open that link.
Your app must claim that it can handle links from your website.
And iOS then contacts your website, and confirms
that your site knows about your app, and is willing
to be represented by it.
So let's get your server ready.
There are four steps in this process.
The first step is to create a special file called the
that iOS downloads from your web server.
This file contains specially structured JSON data.
If you adopted Handoff from the Web
or Shared Web Credentials last year, this should look familiar.
The top level key here is applinks, which is sibling
to web credentials or activity continuation
if you already have either of them.
The apps key is important and should be present
with an empty array as its value.
Under the new details key, you will see the dictionary.
The keys for this dictionary are your application identifiers,
which consist of your team ID, which you can get
from the Developer portal, followed by a period,
followed by your bundle identifier.
If you have more than one app, you can specify each
of them under a separate key.
The paths array tells us what parts
of the website are supported by your app.
If you can support your entire website in your app,
you only need to include a single asterisk
like you see here, but on day one, it's quite possible
that your app won't support every single page
on your website.
So we have given you the ability to specify
which pages it can handle.
As you can see, I have now added two paths to the array.
The first is an exact match.
If a user taps a link that goes to exactly that path,
it will open in this app.
If an entire section of your website is supported
by your app, you can use a path prefix, such as the second entry
in this array, to specify
that that section will open in your app.
Notice the asterisk at the end, which tells us
that this is a path prefix.
It's important to note that these paths are case sensitive
and that other URL components, such as the query
or the fragment, are not supported.
The second step is to procure an SSL certificate
from a recognized certificate authority.
This is not the same certificate that you use to sign your app
for the App Store it's not generated by Apple.
We suggest that you use the same certificate that you use
for your HTTPS server, but it's not required.
If you have more than one domain that you need to represent
in your app, we suggest you consider getting what's called a
wild card certificate.
For more information on certificate generation,
please contact your certificate authority.
Step 3 is to sign the JSON file you just created using your
In order to do this, you will need
to use the openssl smime command in terminal
on your development Mac.
These two file names are important.
The first one is the name of the file that you just created,
the unsigned JSON file that's on your development Mac.
The second must match exactly what you see here,
These are your private key,
which you generated during certificate generation,
and the certificate that was given to you
by your certificate authority.
Once again, this is not a certificate given
to you by Apple.
It's neither your App Store certificate,
nor your developer ID certificate.
There is one last note to make here,
if you have an intermediate certificate that was given
to you by your certificate authority, you will need
to include during signing.
The fourth and final step,
now that you have a signed JSON file,
is to upload it to your web server.
This goes at the root of your server.
For example, www.example.com / apple-app -site-association.
It's important to note that each app
that your domain supports may need a separate JSON file that's
signed appropriately for that domain.
For example, example.com and www.example.com are distinct.
We introduced this functionality in iOS 8,
in support of two features: Handoff from the Web
and Shared Web Credentials.
In iOS 9, we are adding support for universal links,
and we want to make it as easy as possible for you
to adopt universal links in your app as soon as possible.
So-sorry, starting in seed 2 of iOS 9,
we will no longer require you
to sign your apple-app-site-association file.
Simply upload the unsigned JSON -- thank you [applause].
Simply upload the unsigned JSON file with the same file name,
apple-app site-association, to your HTTPS server,
and iOS will take care of the rest.
Note that the signing requirement remains in effect,
if you need to support iOS 8 for backwards compatibility.
If you are starting with iOS 9,
you do not need to fine the file.
Now, let's talk about adoption within your application.
You will need to modify your application delegate,
and you will need to adopt an entitlement.
I will show you how to do both.
First off is the application delegate.
If you adopted Handoff last year, this should look familiar.
This method is sent to your application delegate,
when a user activity is delivered to your application.
User activity objects - excuse me -
user activity objects have a web page URL property that,
in the case of this feature, is always an HTTPS or HTTP URL,
it's never nil for this feature.
The activity type, in the case of universal links,
is always NS User Activity Type Browsing Web.
Once you have confirmed that the activity type matches what you
are expecting, you can break down the URL
from the web page URL using the NSURLComponents class.
Just like with custom URL schemes, it's possible
that the URL doesn't represent anything in your app,
or it represents outdated or malformed content.
If that occurs, you should detect it and fail gracefully.
Now, gracefully here, is app specific.
It may be appropriate for you to present an alert to the use,
explaining what went wrong, or it may be a better choice
for you to open the link in Safari, which you can do
from your app, by calling UI Application Open URL.
The most important thing to take away from this,
is that users are not left staring
at a blank View Controller.
Once your application delegate has been updated, you will need
to update your app's entitlements.
This is easy with XCode.
Navigate to your projects settings,
and to the configurations, sorry,
the capabilities tab there, and select associated domains.
You will want to add an entry for each domain
that your app supports: app links, followed by a colon,
followed by the domain name.
Once again, every domain
that your app supports needs a separate entry here,
they are distinct.
Example.com and www.example.com need separate entries,
as you see here.
I will show you how this all comes
to together in just a moment.
But first I would like to review a couple of best practices.
As with all content coming from outside your app,
data may be malformed and you may need to validate it.
If it is, that doesn't mean that you have made a mistake,
but it does mean that you will want
to fail gracefully when it occurs.
Once you have a URL to process, you should keep in mind
that HTTP has caveats on iOS 9 for the sake of user security
and privacy, if you transport data using, for instance,
NSURLSession, you should always be using HTTPS.
If you need to use unencrypted HTTP in your app,
you should review Session 711, Networking with NSURLSession,
which covers an important new networking technology called App
Now, let's take a look at how this all fits together in XCode.
I will be showing you code
from the WWDC app that's available on the App Store.
So I have here the WWDC project,
and I have created a new extension that's going
to house the code we are looking at today.
This extension is on my application delegate class
and the first thing I'm going
to do is add the application delegate method
that we discussed.
Now, again, if you adopted Handoff last year this is the
same delegate method.
You'll just need to extend it to support this new functionality.
The parameter we are interested in,
is the user activity parameter, which contains information
on what the user is doing,
which in this case is navigating from a website.
We are interested
in the activity type and the web page URL.
For our purposes, the activity type is always NSUser activity
However, if your app supports user activities coming
from other sources, such as Handoff, then you will need
to check the activity type as I have done here.
Because it's NSUser activity type BrowsingWeb,
I know there is a web page URL and that it's not nil,
so I have used an explicit unwrap
of the web page zero property to get the URL from it.
I then send it to this function, present URL,
which is responsible for breaking down the URL
and delivering it to the user interface
so that the user can see it.
The signature of this function is fairly straight forward.
It takes a single URL as input, and returns a Boolean,
saying whether or not we were able to handle the URL.
Now, the body of the method is a little bit more complex,
but I will walk you through it line by line.
The first thing we do is create an instance of NSURL Components.
This class, as I said before, breaks down the URL for you
and allows you to access the individual parts of it,
without you having to build your own parser.
Once we have that object, we extract a few components from it
that we are interested in here.
For the WWDC app, we are interested in the host
and the path components.
Now, the WWDC app can handle URLs coming from more
than one domain, so I want to check the domain to make sure
that it's what I'm expecting,
so I have got a switch statement here,
with a case of developer.apple.com,
which is the URL that Conrad showed you earlier, or sorry,
the host name for that URL.
Once I have matched that, I want to match the path,
and I use a switch statement, which is very powerful in Swift,
to gather all of the components at once, and compare them
in a single line, s/videos,/WWDC,
something that may or may not be a year.
I want to take a look at that value,
which is a string right now, and turn it into an Int.
So I cast it with the Int initializer, excuse me,
and I also gather the session number,
which in this case would be Session 509.
In the case of the example Conrad showed you,
it was Session 101.
Now, this Find Session method I will show you in just a second.
If those components have matched,
then I want to present it to the user,
but I need to validate the input first because it's possible,
it's possible that it isn't in a range that makes any sense.
For instance, a year before Apple was founded
or a session number in the millions.
So I take the year in session, and compare them
against two ranges, 2011 to 2015 for the year, 100 to 10,000
for the session number.
And if it's in those ranges, then it looks
like something that's valid that we can present to the user.
So we proceed to present it.
Now, I mentioned this Find Session method which I'm going
to show you right now.
There we are.
And this is specific to our app.
It's unlikely you will have this exact same code,
so I'm not going to go into too much detail here,
but the idea is that the data that's coming in may come
from more than one source, so we want to check all of them.
The first thing we look at is the fragment, which in the case
of URLs from 2014 and before, contained the session ID,
and then if that didn't contain the session ID, we take a look
at the query which we have broken apart
with NSURL query item.
In this case the query item's property is already populated
We iterate over, and we find the one
that either matches ID, or instance.
Either one may contain the session number for us.
Now, I'm almost done with the code here,
but there is something missing.
There is a lot of return falses here.
There is a lot of cases where we weren't able to handle the URL.
And, again, that's okay because we may get invalid input
from time to time, but up here in my application delegate,
I'm not doing anything with that return value,
and if I get a URL that's clearly invalid,
the user is not going to see anything.
The WWDC Apple opened, and they will be presented
with a blank View Controller,
which is exactly what we want to avoid.
So we are going to fail gracefully
and it's very, very easy to do that.
In this case, if present URL returns false,
then I know that I didn't present anything to the user,
and I pass the URL back
to Safari using UIApplication openURL.
Those are all of the code changes you need to adopt this.
If you have already got code for custom URL schemes,
it's quite likely it will look very similar
to what we have here.
Now, I'd like to show you what this app looked like before
and after we added this code, but before I do that,
I need to add an entitlement.
So I go to my Project Settings, I select the Capabilities tab,
and I select Associated Domains.
I'm going to add a single entry here,
because we are only working with a single domain today,
but once again, if you have more than one domain, example.com;
www.example.com, they will need separate entries
in this entitlement.
In this case, I need applinks:developer.apple.com.
I'm ready to go on my client side.
So I am going to show you what the app looked
like before we added this.
So here I have got my iPad, and this has a copy
of the WWDC app installed before we added, sorry,
before we added universal links to it.
And I'm going to go to the same text message, sorry,
the same iMessage I sent Conrad earlier.
It's exactly the same message, but when I tap it
because I haven't adopted universal links,
it just opens in Safari.
Safari is a great app and it can show me some fantastic content,
and in particular, it doesn't give me all the options
that I can get from the app.
I can watch the video in both the app and Safari, but,
I can also provide feedback from the app, or, make it a favorite
to watch later, I can even download it for offline viewing.
So, as I showed you, we have this now in our app,
in the app you have in your hands right now,
and that looks a little bit like this.
So, once more, this is the URL that I sent Conrad earlier,
and I tap it, and just as before, it opens the keynote
and I can add it to my favorites,
I can watch the video, download it, and so forth.
And that is pretty much everything we had
to do [applause].
So before I hand the remote back to Conrad,
I would like to thank you for coming, and I look forward
to seeing what all of you are going
to create using universal links in iOS 9.
CONRAD SHULTZ: Great.
Thank you Jonathan.
So by adopting universal links you will give all
of your users the best possible user experience.
For users who have your app installed on your iPhone
or iPad, links to your website will open immediately
in your app without any distracting flashes
to detect if your app is installed,
or try to defect it it's installed, and launch the app
on behalf of the user.
If a user does not have your app installed,
your links will still seamlessly open in Safari.
And for the same reason, your links will still just work
on the Mac or even on other platforms.
Now, because this is all handled locally on the user's device,
there are no wasteful extra round trips
out to the user's web server or out to your web server.
And, of course, this also means the neither Apple nor any other
third party can track what your users are doing.
So we have talked a little, or, we have talked
about how these links will take the user to your website
if they don't have your app installed.
Once there, many of you probably want
to help your user discover and install your app.
And ILS has a technology that can help
with that called Smart App Banners.
Let's spend just a couple of minutes talking
about Smart App Banners which is also about the time it will take
for you to adopt them.
So what are Smart App Banners?
They are unobtrusive UI elements that help route users
to your app, and which are presented automatically
by Safari on iOS.
If a user doesn't have your app installed, the banner will offer
to launch the App Store so that the user can download it.
If the user does have your app installed, the banner will offer
to launch the app for the user.
Smart App Banners are trivial to add to your website.
And new with iOS 9's app search feature,
Smart App Banners will be used by Apple to help index your app
and its content, which increases the likelihood
of the user finding and installing your app.
Now, some of you might use custom URL schemes
to launch the user into your app from your website.
This will still work, but it's not recommended.
And there is no guarantee that the user experience
with custom URL schemes will remain the same in the future.
Smart App Banners afford the preferred user experience.
So to adopt Smart App Banners, all you need
to do is add one line of HTML to your website's head section.
The apple-itunes-app meta tag will tell Safari that you want
to install, or display, a Smart App Banner.
The content attribute must include your app ID,
which is used to route the user to the correct app.
You can obtain your app ID using the convenient iTunes
The app argument should include the URL of the page
that is currently being displayed.
This is for a couple of reasons.
First, this allows you to customize the view
that you show to the user.
And it's passed to your application open URL source
application annotation method.
Second, it is, it helps make sure
that app search recognizes your site as more
than just a landing page.
And for more information on this point, I would refer you
to the Introducing Search API session.
You can find the video of it.
It was held yesterday.
Finally, note the Smart App Banners will not appear
in the iOS simulator, so be sure
that you test on a physical device.
Once you have adopted Smart App Banners,
and once your app can handle links to your website,
you have a really cool opportunity
to simplify your website.
It no longer needs to be a promotional vehicle
for your app.
It can instead focus on presenting your content
in the best manner possible.
You no longer need to be in the business of displaying banners
or splash screens, or writing code
to handle custom URL schemes.
Your website can now be simpler to use and simpler to write.
Okay. So we have talked a fair bit about how
to get users into your app.
Once there, many of them are going to have to log
in to your service, for it to be useful.
If that happens, they might see a screen
like this, a log-in form.
This is a frustrating moment for your users.
They probably don't remember their credentials even though
they might have logged into your site many times in Safari.
They don't want, or don't know, how to look their passwords up.
This is precisely the moment
when the user might just abandon ship,
leave your app, maybe even delete it.
But what if instead you could present a list of credentials
for the user to select?
That would turn this into this.
A simple picker that allows the user to choose
from passwords they have previously saved in Safari.
Well, the technology to do this was introduced in iOS 8
and it's called Shared Web Credentials.
Shared Web Credentials allow your app
to access Safari's saved passwords
and thereby eliminate the headache for users,
of r having to remember their passwords
in different contexts on different devices.
How does this work?
Well, when Safari on either iOS, or OS X sees a user logging in,
or filling out an account creation form,
it can help the user in several different ways.
Specifically, Safari can offer to save passwords
on the user's behalf in the keychain.
It can sync those passwords to all
of the users' devices using iCloud Keychain,
so they are always available
on the user's iPhone, iPad, and Macs.
It can suggest cryptographically-strong
passwords for the user, and of course,
it can autofill those passwords on the user's behalf,
when the user goes to log in.
And with the Shared Web Credentials API,
it can also prevent users from being stuck on your app
when they go to log in.
To adopt Shared Web Credentials, there are two parts.
First, you need to establish a tight association
between your app and your website just like we talked
about for universal links.
Second, you do need to adopt a little API in your app,
to access the credentials themselves.
So since shared web credentials allows the user
to share their passwords, we need to establish
that you control both your app and your website.
Just like we did when we allowed your app
to handle links to your website.
Your server tells us to trust your app by a simple addition
to the apple-app-site-association file
that Jonathan discussed.
Specifically, all you need to do is add a top level dictionary
under the web credentials key, with a key of apps, and an array
of app IDs, that your server,
your website trusts to share credentials.
Your app also needs to tell us to trust your website.
And this is accomplished by adding, a,
one or more web credentials entries
to the associated-domains entitlement
that we talked about earlier.
And you can also do this using XCode's project inspector
as Jonathan showed in the demo.
Okay. So now let's talk about adopting the API in your app.
A typical flow in your app might look something like this.
First your app will check to see if it has credentials saved
in the keychain, and if so, use them to log in.
If not, it will ask to access any passwords
that Safari has saved.
If those are available, it will use those,
it will save them locally and then log in.
If none are available, it will present its built-in log-in UI,
to ask for credentials and then proceed in the usual manner.
So a basic implementation
of requesting Safari's passwords might look something like this.
The key function is SecRequestSharedWebCredential
which takes a completion handler,
and optionally takes domain and user name arguments.
In the completion handler,
we check whether we got credentials back,
and if so, use them to log in.
Otherwise, we will present the log-in UI.
Note that in both cases we dispatch to the main queue
because there is no guarantee
that the completion handler is called on the main queue.
So if your app allows the user to create an account
or change their password,
you really should update Safari's keychain
so that the next time the user goes to log
in to your website on, say, their Mac,
the correct password is available to be autofilled,
and you can do that using the SecAddSharedWebCredential
function as shown here.
The Shared Web Credentials API also lets you generate secure
passwords using the exact same algorithm as Safari,
using the Sec Create Shared Web Credential Password function,
as shown here.
There are some additional advanced features of the API,
and if you are interested in those, I will refer you
to last year's session, Your App, Your Website, and Safari.
So we have talked about some great tools
for giving your users the best experience
in both your app and your website.
First, on iOS 9 with just a little bit of work on your part,
regular links to your website can become universal links.
And can open your app just as easily
as they open your website.
And iOS will insure that your users are given the best
possible experience, which may be your app,
or may be your website.
The days of using custom URL schemes to route users
into your app are over.
Next, you can adopt Smart App Banners, which will,
the first time your user visits your website,
will help them discover your app.
On a subsequent visits,
they will help redirect users into your app.
And at all times, they will help App Search index your app
and its content, so that it is available to your users
in both Safari and Spotlight.
Finally, by adopting shared web credentials,
you can give your app access to Safari's saved passwords,
and make logging into your app a breeze for your users.
For more information on what we have talked about today,
you can check out our documentation.
You can ask a question on the Dev forums,
and of course reach out to DTS.
You can also contact John Davis, our Web Technologies Evangelist
or Jake Behrens, the App Frameworks Evangelist.
There are several related sessions, both this year
and last, that go more into detail on some
of the topics we discussed.
Thank you and have a great rest of the conference.
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.