Media • iOS, OS X • 54:02
While a modern digital presence combines both apps and traditional web sites, people expect a coherent, seamless, and device-optimized experience in each case. Learn techniques and best practices for successfully making transitions between iOS and OS X apps and the web. Gain insights into the appropriate web standards you can use to make your web content look and work optimally in Safari.
Speakers: Ricky Mondello, Andrew Whalley
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript has potential transcription errors. We are working on an improved version.
Welcome to Session 506, "Your App, Your Website, and Safari." My name is Ricky Mondello, and I'm an engineer on the Safari and WebKit team. So before we get started, I'm going to ask one obvious question. Show of hands and then a second question. So first off, how many of you have apps in the App Store? Nearly all of you. Great. Welcome to WWDC. You're in the right place. How many of those apps have an associated website? Another show of hands. That's excellent. You are all in the right place. Awesome. So yesterday you saw Craig demo a bunch of cool new features with the name Continuity.
The ability to pick up writing an email where you left off from one device to another. Picking up a phone call from one device to another. I've been thinking about Continuity. Continuity is more than just a suite of features that we've included in OS X Yosemite and iOS 8. Just a regular old word. Looked it up in the dictionary. It's the unbroken and consistent experience or operation of something over a period of time.
I take comfort in this definition because unbrokenness and consistency are exactly what all of us users are looking for today. Many of us own more than one device. We move between those devices throughout our day. Something that's not uncommon is starting a task within an app on your iPhone and finishing it up later on the app's website on your Mac, for instance.
And this year, we've got a cool technologies that you can participate in Continuity. Handoff. That's exactly what Craig used to move from sending an email on one device to typing it up on the other. We've got a full session on Handoff this year. It's tomorrow at 2 p.m. So if you're here, you're going to be interested in that as well.
But even before Handoff, users are making these kinds of transitions between apps, websites and various devices every single day. And in this session I'm going to go over some of the best opportunities that we've identified to make these kinds of transitions smoother for our users. Ensuring Continuity for all of our users is going to require a holistic approach to software development. Bringing together three different kinds of disciplines. The first discipline is that of the App developer, whoever's writing Cocoa, Cocoa Touch, Objective-C and now Swift. Probably most of you in this audience. But it's also going to take the back-end website administrator.
It's also going to take the back-end website administrator. Whoever's running user accounts, state, databases, all that sort of stuff. But finally, it's also going to take the front end web developer. Whoever is writing JavaScript, HTML, CSS or whatever cool new fancy languages preprocessing down to those these days.
So if you're here, you probably specialize in one of these areas. You might know about two of them. You might just be getting started. Or if you work in a one-person shop, you might be responsible for all three of these aspects of your application or service. Regardless, no matter which bucket you fall into, you're going to leave this session today with actionable techniques you can put into place to make your users' experiences better.
Specifically what we're going to go into today is giving your native iOS app access to credentials that Safari has stored for your website to make your users' login experiences easier than ever. Then we're going to learn about AutoFill and how that works on the web and ways to make that better as well.
Then we're going to look at ways that Safari promotes your website and your website's content. Learn how to make that even better. And finally we're going to look at your website and how we can make your website consistent across devices whether the user's viewing it on an iPhone or on a Mac with a 27-inch cinema display. That sounds good to you? Let's get started.
First thing I want to talk to you about is credentials. If your app or service has accounts, you're defining the credentials these days for your users to prove that they are who they say they are. Today most credentials are in the form of user names, passwords. And as a matter of fact, my colleague Andrew has been working on a cool new application on the side.
It's called "Shiny," and this application has accounts. What it does is, every single day that you log into "Shiny," it's going to show you a picture of a shiny object, but only if you're logged in. That's very important to him. Don't understand it but it's going to be okay.
Something that I realized while I was beta testing Andrew's app is that my first run experience with the app wasn't as good as I wanted it to be. I started my relationship with "Shiny" by creating an account on the "Shiny" website, and then later I went to the app for the first time and I got this login screen, and I couldn't do anything until I got past the login screen.
When I run into a situation like this, I might go ahead and look up my password in settings on iOS, or, if I'm feeling a little lazy at the moment, I just might switch back to using the website or leave the app altogether. We don't want people to leave your apps. We want engaged, happy users. So today I'm very happy to say that we're going to solve this problem by giving your application access to some of the data that Safari has stored for your website, the user names and passwords that are saved for your website.
Safari's technology for saving user names and passwords is called AutoFill. And I'd like to give you a little background on that right now. First thing that AutoFill does is that it fills out forms for you. Kind of like the forms that you see when checking out of the online shopping site with information from your contacts card.
AutoFill also saves and fills user names and passwords to make logging into your websites a breeze. And finally, AutoFill generates passwords for users. Given the state of website security these days, with major websites being compromised what seems like every other week, it's a really good idea to use a random password that doesn't overlap with passwords you're using on other websites. And finally with iCloud Keychain, AutoFill syncs your passwords across all of your devices so that it's a breeze to log in no matter which device you're on.
I really love it when my credentials are syncing across my devices. When those credentials are randomly generated, I am in a sweet spot of both security and convenience. But I've already mentioned one place that the system breaks down. And that's when your application enters the picture. When a user hits an apps login screen anything can happen.
They might look up their password. They might already know it. They might decide to leave your app. So what we want to do today is take this failure point and turn it into something awesome, letting your applications leverage the security and convenience of iCloud Keychain and Safari AutoFill.
For your users, it's going to mean going from seeing an application login screen that looks like this to seeing a simple picker where they can securely and easily hand credentials back to your application that you can use to attempt the login. No typing required. To get into the details of how this works, I'm excited to invite my colleague Andrew Whalley to the stage. Andrew.
[ Applause ]
[Andrew Whalley]
Thank you, Ricky. Yes. I'm Andrew Whalley from Core OS Security Engineering, and maybe predictably for a security engineer, I'm going to start off by talking about password security. Passwords are some of the most sensitive and important data that we have on any of our devices. Passwords to banking websites have access to all our money, and passwords to even something like social media - well that's really our entire online reputation. So we've really got to protect them.
As we visit websites in Safari and let Safari save user names and passwords for us, it's keeping track of which websites they came from to make sure that they are only ever filled back to the exact same website. This security and actually privacy as well guarantee is provided by Safari itself since it has all the necessary information about which credentials went with which website. And I'm using credentials here to mean a user name and password pair.
I'm sure like me you have hundreds of native iOS applications installed. And these are all run and managed by iOS. So what we need to do is provide a way to let iOS know that a particular app is associated with a particular website and to do so in a secure way. So I'm going to be talking about a way to associate your app and your website. And it's really quite easy. It involves adding one file to your website and one new entitlement to your app.
Once you've done that, then you've got access to a few simple APIs, which allows you to see if your user has anything saved in Safari, and if so, gets the user name and password. And to let Safari know if a user has created an account or changed their password within your application.
So really what we're doing is trying to make sure that the website developer and the owner of the website are really OK with their two entities sharing information. Luckily we can use cryptography for this subject. My favorite, favorite subject. Every iOS developer has a key and a certificate issued through the developer portal or now automatically by Xcode. Every website has or really should have a key and certificate issued by a CA which they use to secure HTTPS TLS websites. First we're going to look at the app side of things.
So in the same way that Safari is maintaining internally a mapping between a credential and which website it came from, you can imagine iOS having a similar table linking apps and websites and whether they have been approved to share information between them. You can see here that two have been mapped as approved, and one is pending.
So let's look and see what it takes to go from pending to approved. Whenever your app is installed, it gives iOS a list of all the domains it wishes to share with. iOS then appends apple-app-site-association to the end to form a URL and request it from the server.
The server then responds with a signed file listing all the applications that it wants to share with. iOS then verifies that signature and check the two app names match. Once that's occurred, we know the app name and the website name and we can go complete our table and marked it as approved.
So that's the flow. Let's go into a little more detail. I mention that apps include a list of domains. They're included in the app's entitlements. An entitlement is a signed key value that's included with every application. It's signed so iOS knows the developer really intended to include that information. Anything that's added or removed breaks the signature, and code signing won't let it run.
Whether you know it or not, your application already includes the entitlement application-identifier. It's added automatically by Xcode. And its value is in the form of teamid.bundle-identifier, and it looks something like this. Of the two parts, the team ID is a unique ten character string that's issued to every developer, and you can look it up on the developer portal. The bundle-identifier part you actually specify yourself from within Xcode in the general identity section.
Together the team ID and bundle-identifier create an application-identifier that's unique for everything single iOS app out there, so we can specify we really want that one. Today in iOS 8, we're announcing the new associated-domains entitlement. Its value has the form of service:fully.qualified. domain.name and optionally a port. It will look something like this because for this feature we want the service of web credentials.
It's easy to add values to this entitlement. There's a new section under the Capabilities tab of Xcode associated domains. And actually that's all you need to set up that association on the app side of things. Let's look at what's needed service side. As I mentioned, iOS is going to be requesting the apple-app-site-association file.
But if we just get that far what's in it? Well, it's just a big binary blob, but inside is a very simple bit of JSON. There's a dictionary which contains a list of application-identifiers. In the same way you could list multiple domains in your entitlement, you can list multiple application-identifiers in this file, which allows a really flexible many-to-many relationship.
The content type returned by the web server needs to be application/pkcs7-mime, which means it's been signed. The signature is provided by CMS, that's the Cryptographic Message Syntax, which is the same format used by S/MIME. Let's do a quick look at what it takes to sign the file. So here's the JSON you saw a moment ago but with all the white space removed, and I'm saving it off into json.txt.
Next I'm providing it to open SSL's S/MIME command and feeding in the certificate and key provided by the CA for our site. This is almost certainly going to be the same that is used by a web server, but it doesn't have to be as long as it's valid for the site being associated, in this case, example.com.
The output then is the apple-app-site-association file you can copy straight to your web server for hosting. And when that's there, that's everything you need to do. Except iOS is going to be making requests of the file from the web server, and web servers can return different codes, so let's have a look at some of the statuses.
If the file exists and we get 200 okay, iOS is going to take the file and check the service name and application-identifier match and the signature is valid. And if it is, it marks that app domain pair as approved. If it gets 200 okay, but there's a problem with any of those checks, we go and mark it as denied. Likewise in the 300 or 400 range, like 301, 302 Redirect, or 404 File Not Found, we're going to go straight ahead and mark it as denied.
Anything in the 500 range, like 500 Internal Server Error, we're going to just assume there's a temporary glitch with the web server and retry. It's worth noting that this association and approval state is removed any time the application is uninstalled by the user, which is really useful if you're developing and testing and need to go through this a few times to get it right. So now we've set up that trust relationship between our app and our website. Let's see how we can integrate prompting for Safari's saved passwords into our app's application flow.
Here we are on our home screen. The "Shiny" app just installed and waiting to be launched. Once launched, it's going to go through this flow. Because we need a user name and password to log on to our back-end server right away, the first thing it does is check to see if we already have any saved. They could be saved in the Keychain as a user name and password, could be an auth token or a cookie that we can use to log in. Because we've just installed it there's nothing there, so we're just going to have to prompt the user.
Once the user has entered their credentials, then we're going to save them for accessing them next time and then we can log in and see a wonderful "Shiny" teapot - tea also another one of my favorite things. So next time we come back and launch the application, our check for saved credentials is going to be successful and we can log straight in.
Now let's go back to see what the first user experience flow is like when we can check the saved credentials in Safari. We're just inserting another step. Again the app is going to check if it's saved anything itself, but if there isn't any, we're going to be prompted to see if they have saved anything in Safari.
If they select their credential or they select an account, the user name and password is going to be returned to your application which can then exchange it for an auth token and also save it in the Keychain whatever's required by your back-end server to authenticate. And then you can log straight in.
It might be worth noting that you still need the user name and password view as a fallback in case the user hasn't saved anything with Safari or declined to select one. So that's the flow. Now let's look at the APIs that you can use to implement this. We're introducing three today. SecRequestSharedWebCredential, SecAddSharedWebCredential and SetCreateSharedWeb CredentialPassword.
The first is what causes the account picker be displayed. It has three arguments and a completion block. The first allows you to specify domain name, the second a user name, if you already know it somehow, and then a completion block, which will be run after the user has selected an item, or it has not been displayed for some reason.
Now you've already listed all those domains in your entitlements and you have usually no idea what your user names your users have. So what you can do is pass NULL for both of those first two fields. Passing NULL for the domain just uses the entitlement values. And passing NULL for the user name will get anything that can be found for any of the domains in the entitlement. So whenever that code is executed, we get the picker, user selects a credential, and the completion block runs.
Into it is passed a data structure credential which contains the user name and password and maybe an error if anything's gone wrong. So let's look at some of the code you might want to include in such a completion block. And it's again pretty straightforward. We're just checking that there was no error, and a credential was returned.
We extract the user name and password from the data structure and pass it off to the method that our application already had to log in to the server. Because this is running in the block, I'm going to run that on the main queue just in case it needs to deal with UI.
Our error condition down here will run if the user didn't select the password or there's just none there. And we're going to show the login UI, which is our fallback, and again dispatch that to the main queue. And that's really all there is. However your application might allow a user to create an account from within the native application, and in this case, we need SecAddSharedWebCredential.
This time you must specify a domain name and a user name and the password. There's a completion block, but an error in this case usually means there's something amiss with your app site linking and the approval process, so it's more useful during development. If there are no existing accounts in Safari when you call this, this completes with no UI. It just goes straight in. Which makes it really easy to include in your flow and make sure those credentials are just there the next time the user goes to your website in Safari and with iCloud Keychain on any of their devices.
If the user can change their password within an application, you make exactly the same call but just supply the new password. In this case with an existing item, we're going to prompt to make sure that's what the user wants. To delete an account, you just pass NULL as the password argument. Again the user's going to be prompted.
Note you should only delete this if they really are deleting their account. If they're just logging out then they're going to need their credentials next time they log back in again. Or maybe you've already determined that those credentials are stale and they would never work anyway. So the final API simply returns a random password each time it's called. It's in the same format used by Safari. Returning for a moment to the completion block, there are a number of errors that could be returned, and we'll have a look at them now.
The first is errSecParam error. And a description of "No domain provided." And what tends to be happening here is you're passing NULL as your domain so we're going to look at the entitlement. But the entitlement isn't there, so we get the "no domain provided" error. You can also get a missing entitlement error, and this means you are specifying a domain name but there is no entitlement.
Next you could get a SecItemNotFound error. And this means that there is no proved domain, which is the saying you get if there just aren't any, but you are approved. We're returning the same error condition for both of these as a privacy feature, which means that rogue applications are never going to get any more information than they really deserve. So now let's have a quick demo of putting that all into practice. So first of all let's go to the "Shiny" website. But I haven't actually gotten an account yet, so I'm going to sign up.
Safari is suggesting to fill a password, so I'm going to accept that, and oh, today Shiny's thing is macaroons. That's going to go very well with the tea. So let's go over to iOS and have a look at iOS native "Shiny" app. Well, here it is. Going to launch it. And here we are. User name and password. So it was Andrew, and I actually have no idea what it was, it came up too quickly. So let's go back and see if we can change the application to fix that.
Here we are in the Log On View Controller. And if you scroll down a bit, we've got the method loginWithCached CredentialsIfPossible. It in turn is calling loginWithLocalKeychain CredentialsifPossible. That takes two blocks. The first will be passed the user name and password if there's one in the Keychain. And the second is what's run on failure. There's no credentials saved. So I'm going to create a new method to parallel this one.
LoginWithSafari CredentialsifPossible. Again taking a success block and a failure block. So this code is very similar to the one I walked through in the slides, but instead of calling a method directly, we're calling the success block and the failure block if there was an error or the user declines to select anything. So now I'm back in the error block, the failure block of our previous method. And instead of the back, fallback being show log on UI entitlements I'm going to put a call to our new method there.
And in this way we have a cascade of blocks, so if there's nothing found in the Keychain, it calls a failure block. If there's no Safari saved credentials, it calls its failure block, which falls back to the login UI. So let's give this a build and see what happens. So we launch it, and yes, we get a picker. Log in and there are today's macaroons.
The app did provide a way for us to change a password, and it's already prefilled one out from the SetCreateSharedWeb CredentialPassword. So I'm going to accept that. That looks reasonably secure. Change password, and because I already had something in the Keychain, it's prompted me. So I'm going to go ahead and update. There we are. Logged straight in. And now I'll come back to the Mac. All being well, yes, there we go. iCloud Keychain has done its magic and the user name and password - yes iCloud Keychain has done its magic. The updated password is over there.
[ Applause ]
So, though there are quite a few steps, none of them are particularly difficult and it allows - well, it's maintaining a very, very high level of security - this last broken point in the convenience and security ecosystem to be made complete. Thank you all.
[ Applause ]
Thanks, Andrew. I am really excited to see what you guys are going to do with these APIs. It's really a personal pet peeve of mine to get stopped in my tracks at a login screen, and I'm really excited that if I've gone to Safari, created an account there, to never see that again.
SecAddSharedWebCredential, the API that Andrew told you about that you'll use to add a password to the user's Keychain, update a password in the user's Keychain or delete something? It's really neat. It's not neat just because of what it's going to mean for all of us in this room as users. But because it perfectly captures your application's intent.
Your app tells iOS exactly what it wants to happen to a user's credentials, and they're automatically synced across all of the user's devices with iCloud Keychain. It's not as clear cut on the web however. Your website doesn't have a way of saying, "Hey, Safari I just updated Andrew's password from Foo to Bar." Instead, what Safari does is it uses a series of heuristics in order to figure out what your website's doing and then update your user's passwords accordingly.
And to make this all work, Safari needs to understand your websites' forms. So here's a flow diagram of how conceptually a password manager could be looking at your websites. Users move between states like not having an account, registering for an account, having the account and logging in and out of it.
Safari does all of this automatically. There's nothing that you have to do. There's no opt in for Safari analyzing your website and offering password features to its users. Because this is automatic and it's using heuristics, things can go wrong. Heuristics aren't perfect. And when things do go wrong, Continuity breaks down for your users.
For instance, if Safari didn't have a password saved for your website when you call SecRequestShared WebCredential from within your iOS app, you're going to get back an empty array. And if Safari didn't notice that a user had changed their password on your site, if Safari didn't understand that that happened, when you call SecRequestShared WebCredential you're going to get back a stale credential, and your users aren't going to be logged in effortlessly.
Because of this, it's in everyone's best interest to test their websites with Safari AutoFill. Fortunately, it's really straightforward to do this. All you have to do is pretend to be one of your users for a few minutes. So let's go back to the state diagram. The first thing you're going to want to do is get yourself a clean device or a clean user account because you don't want to interfere with whatever credentials you personally have stored for the websites that you're building.
And then the first thing that you're going to do is create an account just like your users would. Go to your account - website's account creation forms and sign up. The two things that you're looking for here are the Safari offer to generate a random credential for the user, a random password, and then after you've created the account, Safari's saved the user name and password.
And the way that you're going to check this is by on OS X opening up Safari's preferences and going to the Passwords pane, and on iOS, you'll open Settings and go to the Safari section, and you'll have to find a list of passwords and user names that have been saved.
All right. Now you have an account. So now that you have an account and you're logged in, the first thing you're going to do is log out and then try to log back in again. And when you go to your website's login page, the thing that you're looking for is that Safari prefilled the credentials so that the user doesn't have to type them in.
All right. We've done that. Next up, the user's going to change their passwords at some point during their relationship with your website. So you're going to want to do that when you're logged in. Go to your Change Password form, and while you're there, make sure that Safari offered to generate a credential for your users and that it saved the updated credential appropriately. Go back to that list and make sure that the new credential is saved. And then finally if your website offers a way for users to change their passwords when they have forgotten them, go ahead and check that too. Make sure that Safari saves a good credential.
And then finally, to simulate a user having started their relationship with your website outside of Safari's watch, delete the credentials that you've created for this website. Go into Settings. Go into Safari's Preferences, delete them. Then go back to your login page, manually enter them and make sure that Safari saved them at that point, so that your users have a great experience.
All right. Testing's over. Hopefully everything worked perfectly, but if it didn't, I'm happy to say that we have two new strategies to tell you about today that you can put into place on your website's forms to help Safari understand your website better. The first one is the about declaring your forms' intentions.
New in Safari 8 on Yosemite and Safari on iOS 8, Safari supports three new values for the autocomplete attribute. User name, current-password and new-password. These are a relatively recent addition to the HTML specification, and they're really easy to use. Just label your user name fields as user name, any field that's asking for the user's current password is current-password.
And you guessed it, a new password is new-password. So let's walk through a few examples of this. Here's a standard login form. You're probably asking for the user's user name and password. Go ahead, label them, input type "text." User name autocomplete is "user name," and label the password field current-password.
On whatever form users create accounts. It's pretty similar. Just label the fields as user name and new-password, and Safari's going to understand your forms a little bit better and be sure to offer password generation for your users. If you want your users to confirm their passwords to make sure they entered them the right way both times, that's fine.
Just go ahead and label both your password field and your confirm password field as new-password and Safari's going to understand that better as well. And then now changing passwords. That's fine. You're going to want to know the user's current password to prove that they are who they say they are. And you're going to want a new password because that's the point. Just use current-password and new-password.
Something that you might notice about this slide is that I'm specifically outputting the current user's user name within the form. This is a really good idea because what this does is it tells Safari which user is having their password updated. This is really important if users have more than one account on your site, but it's just a good idea in general.
Now if your design doesn't accommodate this sort of thing, putting the user name out in plain text read only like this, don't worry about it. Just change the input from type "text" to type "hidden." Your users aren't going to see it, but Safari is, and Safari is going to understand your forms better and do a better job insuring continuity with respect to your users' credentials. And then finally, the forgotten password case. In this case you might want to know their user name, same thing as before. Same caveat. You can hide it, and this time all you want is a new password, so label it that way.
These three new values add to an already long list of values that you can use with the autocomplete attribute to help Safari understand your forms and your intentions better. So for instance if you have an input element and you label it autocomplete phone, when the user uses AutoFill to fill out the form, you're going to be saying "hey Safari when you're AutoFilling this, please put the phone number here - please don't put anything else," and Safari's going to do it right. Okie doke. That was the first technique I want to tell you about to make Safari understand your forms better, and it's pretty universally applicable. I mean you can use it on any website that has forms.
The second technique I want to tell you about is a little more specialized. Specialized because it has to do with a certain type of web application, and that's the kind of web application that loads content dynamically via XHR and using JavaScript. You know these pages. They don't do a full page refresh when you click on a link; everything just snaps in instantly. These pages are great for users. This dynamism is awesome. But it's that same kind of dynamism, no full page transition, that makes it a little bit harder for Safari to understand that one of these transitions in the password state diagram had happened.
Fortunately, using some of the same APIs that you're probably already using to build these web applications, those APIs are going to tell Safari that something interesting had happened with the users' credentials. What we're talking about is history and history's pushState and replaceState methods. What you do with these is you contribute to the browser's back-forward list, so if I'm a user on your site and I click three links when I hit the back button, if you're not using this, you go back three pages, not a single page. If you're using pushState, you're adding state to the back-forward list and you only go back one page.
Specifically, pushState and replaceState take a data object which represents the current state of your web application. This is what you're going to get back when you listen for the PopStateEvent. When the user hits the Back button, that event will fire, and you'll go ahead and re-set up the state as if the user had just hit Back and loaded a whole new page.
As a user it's really easy to think about the back-forward list as a left-to-right or right-to-left series of pages. But I want to encourage you to change your thinking on this. Really, what the back-forward list is, is just a stack of user state, and with pushState and replaceState, your dynamic HTML5 JavaScript-driven XHR never reload awesome web application can go ahead and add to it.
So the next time that a user is on your dynamic website and they go to change their password, just call pushState once the password change has succeeded. This way, Safari is going to know that it's a really good time to prompt about saving an updated password, saying: "It looks like you just changed your password.
Are you sure you want to do this?" And remember, the history API and everything I just showed you really only comes into play if you're building one of those dynamic JavaScript-driven applications. If when you click on a link or do something else a full page reload happens, you don't have to worry about this.
So, by testing your website with Safari AutoFill, finding any problems, and trying to fix them with the two new techniques that I told you about, you're going to do a better job insuring continuity with respect to a user's credentials. Their passwords will automatically fill, and they'll be logged in on all their devices in Safari. And your iOS applications will have access to great passwords in SecRequestSharedWebCredential. But if these two techniques don't solve all of your problems, what I'm going to do is encourage you to please file a bug.
If you file a bug, we might be able to give you a workaround for your problem. And regardless of whether we can give you a workaround, your real world test case, your problem, is exactly what we need to make Safari's heuristics better. Real world test cases make everything better for everyone, so please file bugs. I promise that we'll use them.
So that was AutoFill and ensuring continuity with respect to your users' credentials. The next thing I want to tell you about is ways that Safari promotes your website. As of iOS 7, a Safari user's start page is a nice friendly easily tappable list of a user's handpicked favorites. And on iOS 8, we've augmented that view to contain a few sites that the users visited frequently.
And Safari 8 and OS X Yosemite share that same start page design. The quality of the icon for your website that shows up here can make a huge difference. It's a first impression, and it can make all the difference between a user promoting a frequently visited site to a favorite or removing it outright. And I bet that you and your teams sweat over your applications icons on iOS for what you submit to the App Store. And it's time to do the same exact thing for your website. So let's get into the details of how to specify these kinds of icons.
First up, Favicon. Your website probably has a favicon. It shows up in the address bar next to a website address on OS X. Websites have been specifying favicons for a long time. It's really easy. A little bit of markup in your pages head section, or a standard path on your file on a server, and Safari's going to fetch that.
But for the start page on iOS and OS X, what you're really going to want to specify is what's called an apple-touch-icon. You might already be specifying this because this is the icon that's used when a user adds your website to their home screen on iOS. It's really easy to do this.
A simple link tag inside of the head section, and you can even specify multiple of these with different sizes so that an application like Safari only downloads the one that it needs. Downloads the lowest resolution one possible, conserving bandwidth for your users. For information on the sizing, I'm going to encourage you to check out "Configuring Web Applications" on the Safari Web Content Guide.
So the reason that we're using Apple Touch Icons for the start page is because there are already a ton of them out there and they're already the right resolution to be shown an a Mac and an iOS device. Don't let the part where there's touch in Apple Touch Icon weird you out with OS X. Just think of this as your website's icon.
And about the icon itself, use every single pixel in a square canvas available to you. Safari is going to show all of those pixels, and if a user goes ahead and adds your website to their home screen on iOS, where you know that icons are rounded off, iOS is going to handle that for you so there's no need for you to mask the image. We don't want to have mismatched corner-radii.
And finally now that Safari on OS X is using Apple Touch icons, you're going to want to put the Apple Touch icon markup on all of your pages. Not just the ones that you're serving to mobile devices. There's really no cost to doing this little bit of markup, and only devices that care about the icon are going to go ahead and download them. All right. So that's icons. The first impression of how your website is seen by users in Safari.
Let's talk about other ways that Safari promotes your content. Safari today is more than just a way that people browse the web. Safari users add pages to read later using Reading List. They ask Safari to clean up articles for them to read later or right now using Safari Reader.
And on OS X Mavericks and iOS 7, a feature called Shared Links lets the user find articles to read, find other stuff that's interesting that were shared on social networks. Well, new to Safari 8 on OS X Yosemite and iOS 8, your website can participate in shared links.
This works, as you might expect, using your website's RSS feed. So as soon as the user chooses Subscribe and Shared Links on iOS or OS X, Safari's going to periodically fetch your feed and show that content to users. The content that's shown in the sidebar is directly taken from your RSS feeds.
Something to note here is that you want to serve your RSS feeds and the markup inside of your pages head sections that let browsers detect them to all devices, even mobile ones. Safari on an iPhone is just as capable of participating in shared links as Safari on a Mac.
Let's turn our attention back to Reading List. Even if your site doesn't have a RSS feed, users can add your content to their Reading Lists. And as you might know, many of you in the audience, as of iOS 7 your application can add to a user's Reading List.
It's a piece of cake. All you have to do is import the Safari services framework and call a single method that takes a URL, a title and some preview text. Because you're specifying the title in the preview text, you're in complete control. But hold up. This situation should sound a little bit familiar to you.
Yet again it's not always clear cut on how to express these sort of concepts on the web. A page on your website doesn't have a way of saying, "When I'm added to Reading List, use this as my title and this as my description." It can't say exactly that, but there are things that your web pages can say that help them out. So let's look at an example of this. Here's some markup taken from the WebKit blog, a recent blog post about the fourth tier, LOVM JIT that makes JavaScript and Safari superfast. I encourage you to read it. It's an awesome blog post.
That title doesn't really work as the title of a user's page because it has those bread crumbs, Surfin' Safari archive. But here using the open graph metadata standard, I've put markup on the page that clearly expresses a good title and a good description. It's really simple to use.
And when your website speaks with intention this strong, Safari is going to listen. Because you've expressed the exact information that Safari is looking for, Safari can rely on you rather than using heuristics to get the job done. There are other ways of specifying the same sort of data.
Here's some markup you might be familiar with if you're a web developer, meta name, description. This is one way that you might be specifying a description to let's say, search engines for your pages on your site. Safari can listen to this, too, because the concepts are closely aligned: your site's description and a Reading List description.
OpenGraph is just one metadata standard that you should be checking out today. There are a bunch of them on the web. And this example with Reading List and getting better titles and descriptions is just one example of how metadata can help you out today. Browsers, search engines, other websites. They're all going to find uses for this metadata in the future as they display your content to try and make it more attractive to users. So I encourage you to check these formats out and think about adopting a metadata strategy.
Okie doke. Checkpoint. By now, you've added the simple file to your web server. You've adopted entitlements in your iOS application, and you're ensuring continuity with respect to your user's credentials by making it easy for them to log in to your app. You've tested Safari AutoFill. You've made it work great on your website.
Your icons are awesome. Just as good as your app icons. And you've adopted some metadata so that websites, browsers and search engines understand your web content better. For the rest of our time together, I want to go over a few examples of how we can make your website have a great experience when transitioning between devices. Users viewing your websites on mobile devices and desktop devices.
The first example of this that I want to talk about is called The Sticky Mobile Website. So let me tell you about a problem that I just ran into the other day. I have this all the time, but it actually just happened last week. My friend was reading an article on her iPhone, looked good to her, and she thought that I might like it so she sent me a link to it. I was sitting at my Mac and I opened it up and this is what I saw. I saw the website's mobile layout stretch up super wide with lines of text that are completely unreadable.
On other websites when I've run into this problem, I've seen this version of it, where the content is constrained to say, 320 points. This isn't readable either. So what I was able to do with this problem was look at the website's address, the page that I was on.
And notice that I was on a mobile specific subdomain of the site, and by removing the m dot from the URL, I can get a version of the website that looked great on a desktop with a nice constrained width. So some of our users are going to be able to figure some of this stuff out, but they shouldn't have to, and most of them won't. So let's get to the bottom of this problem and figure out how to fix it.
So here's what I think what happened. This is a concept diagram. It might not be exactly the same from site to site, but it's the big picture. And what happened here is that the user, my friend, went to a link on her iPhone, and when she went to example.com in this case, the website said, "Hey, I can tell you're on an iPhone from your user agent string.
I'm going to send you to a mobile version of the site because I think that's better for you." And then she sent me a link to that mobile version of the site, and I went straight to that mobile site. It didn't have the kind of same courtesy to route me back up to the desktop version of the site. And now with Handoff on OS X Yosemite and iOS 8, it only takes one person to reproduce this problem. You don't need friends sending links to each other. So there's even more reason than ever before to get your URL strategy together.
The solution in this case, I alluded to it earlier, is pretty simple. Let the mobile website route users back to the desktop website, giving me the version of the website that's appropriate on my 27-inch cinema display. So this would have solved my immediate problem, but there is a much better solution to be had.
And that comes by admitting that the time of the mobile website is over. Using technologies like Responsive Design and Layout, HTML 5, CSS, you can make one version of your website that looks awesome for all of your users whether they're on an iPhone, an iPad or a Mac.
I'm happy to say that we have a full session on Responsive Design this year, and you should check it out. It's Friday morning. And if you're interested in this and you have a mobile website, you're going to want to go to that. Okay. One more topic to talk about. And that's video.
Since the dawn of iPhone, users have been grading - getting amazing video on their iOS devices. And that amazing video has been coming to them without the use of any browser plug-ins, using standards based video. And because of this, users have been getting excellent battery life and smooth video playback even on relatively low-powered devices.
For a consistent unbroken experience for how users get video on your site, you should use the same video strategy on OS X. It works just as well there. Give users better battery life and smooth playback. And if the reason that maybe you might be avoiding losing the plug-ins on the desktop is because of maybe pre-rolled advertising of some other interactive component, you can do that today with JavaScript HTML and video. It's really simple. For more information on Advanced Media for the Web, you should check out - actually you should have checked out this session this morning at 11:30. Go back and watch the video if you weren't there.
Okay. So in the last 50 minutes, we've covered quite a bit. And all of it has been about ensuring continuity: unbroken, consistent experiences for all of our users, whether they're using our products and services with an app, with a website and no matter which device they're on. And so to recap, here's what I want you all to go out and do today.
First things first. Put the signed JSON file on your server, add the entitlements to your application, and call the APIs to make sure that nobody in this room ever sees a login screen on an iOS app when they created their account on the website in Safari and Safari has credentials.
The next thing to make sure that those credentials that you're getting back from the SecRequestSharedWebCredential are always up to date, test your website with Safari AutoFill. Everything might work great, but you should run into the problems before your users do, if there are any. And if there are, look at the two techniques that I told you about today.
No matter what kind of website you have, you should adopt the AutoComplete attribute and use user name, current password, and new password to make it easier for Safari to do the right thing with your forms. It might only take five minutes, and it will make a huge difference for your users.
Then sweat the details on your application's icons. Make your website's icons look as great as that of your apps. And then look at metadata. Find ways to make web browsers, search engines and other websites understand your content better for when they're showing it to users. And then think about your desktop and mobile websites. If you have two different versions of your website, try to have one.
If you want more information on any of this, I encourage you to get in touch with Evangelism, DTS or the Support Forums. And we've got some really cool related sessions this year, some of which I pointed out earlier. "Advanced Media for the Web," Touch ID and the Keychain, Adopting Handoff to make these smooth transitions, just like you saw with Craig and Mel, and Designing Responsive Websites. Thank you so much everybody. I hope you're having an awesome WWDC.
[ Applause ]