[ Applause ]
Good afternoon everybody.
Welcome to session 220, customize loading in WKWebView.
My name is Brady Eidson.
I'm a software engineer on this [inaudible] teams at Apple.
A little later I'll be joined up here with my colleague, Alex Christensen.
Let's just start out with a quick question.
How many of you integrate web content into your applications?
You're probably all in the right room.
Now a follow up.
How many of you have used WKWebView to integrate that web content?
Fewer hands came up.
Still a lot of you though.
So I'm very excited to talk about some great new APIs in WKWebView that will help all of you who are already using WKWebView do some cool new things and hopefully adopt help the rest of you to adopt WKWebView.
Apple is a big believer in web technologies.
We know how important they are to our developers, our users, and your users.
We have a handful of different technologies you can use to integrate web content in your application.
Today's talk is primarily about WKWebView but I do briefly want to mention a different one which is Safari View Controller.
If you need an in app web browsing experience, Safari View Controller is, by far, the preferred way for you to go.
Using just a few lines of code, you can integrate a powerful secure web browser into your app.
You don't even need to worry about adding the UI.
But underneath that UI is web content.
And many of you have much deeper needs than a straight forward web browsing experience can fulfill.
You need customization of the behavior of the loading of the web content of how it renders, of how it interacts with your native UI.
In fact, many of you might be integrating the web content into your native UI in such a way that it's not even obvious you're using web content at all.
If this is you, then WKWebView is, by far, the preferred way to go.
Now I want to briefly talk about WKWebView's architecture.
When you integrate web content into your application using WKWebView, you are inviting the entire web platform into your application with all of its power, all of its flexibility but also all of its complexity.
So WKWebView isolates your application from this complexity as best we know how.
It does this using process isolation.
This gives us some fantastic security benefits.
We can protect your application from potentially malicious web content.
In fact, different bits of web content can each run in their own web process.
So we can protect your trusted web content from other potentially malicious web content.
This also opens up some great performance benefits.
But because all this is happening in a separate process, the normal steps you would take to configure your process don't apply to the web content.
We need explicit APIs to interact with the web content.
And every since we introduced WKWebView, we've been gathering feedback from developers of all types working on all sorts of applications.
And we've compiled a list of a top three that we're going to introduce today.
And we think that these three new mechanisms will unlock some pretty powerful capabilities that you guys have been asking for.
So first we're going to introduce a way to manage cookies visible to a WKWebView.
We're going to introduce a fantastic mechanism for filtering unwanted content from your WebView.
And finally, a way for you to provide custom resources to web content no matter where you have the data for those custom resources.
Wherever no matter where it comes from.
So first managing cookies.
We actually know that a lot of the developers dipping their toes in the waters of WKWebView are pretty new to the web platform.
So I briefly want to go over what is a cookie?
When a webpage is rendered in a browser engine, a lot of sub resources come up.
For each sub resource, we need to make a request to the server.
The server needs to respond with the data of the resource itself and some meta data about the resource.
And both the requests and the responses include little bits of data called cookies.
The server will respond with a cookie which the browser remembers and can send back out to the server in future requests.
This helps track a user's session when using a web application.
Things like their log in credentials.
Their log in state, a log in token.
Or if it's a shopping application, perhaps, the state of their shopping cart and what's in it.
When you're integrating web content into your native application, you often need to manipulate these bits of data.
You might know things ahead of time to help prompt prime the WebView.
To make the experience smoother.
You might need to do something like add a log out button where you could just delete the session cookie or the cookie representing the user's session log in.
There's also other another type of why you need might need to manage cookies which is the opposite of helping the session along.
You might need to protect the user from having a session tracked for certain types of applications and perhaps, certain types of users this is becoming more and more important to be aware of these days.
So whatever your reason is to manipulate the cookies visible to a WKWebView, the new API, WKHTTPCookieStore, will do what you need it to do.
Using the HTTPCookieStore, you can add and remove individual cookies visible to a WebView.
Pretty straight forward.
You can also get a list of all of the cookies visible in the WebView.
Now that is possible.
And then finally it adds a mechanism for you to observe changes to the cookie store.
And you can now observe those changes.
So you can get to a specific WKWebView cookie's store through its website data store.
This is already a property that's exposed on a WebView's configuration.
And now the website data store itself has a new property to get to the cookie store.
To add a cookie, you use the already established HTTPCookie API to construct the cookie object you'd like to put in the data store.
The specifics of this are up to you and the web service you are interacting with but once you have the cookie all set and ready to go, you'll call set cookie on the cookie store with your cookie.
Now you'll notice there's your trailing closure here, a completion handler.
This process is asynchronous and it needs to send out to all those processes involved in the WKWebView mechanism that are isolated from your application.
So you need to wait until WebKit decides it's all done and ready to go.
And you can be confident in your completion handler that any request you load in the WebView will have that cookie applied.
If you need to manipulate the existing cookies, you can get at the set of all cookies.
Again in your completion handler, you'll have a set of the cookies and you can walk through and find the right one.
In this illustration, we're going for logging the user out by finding the log in cookie.
And then we'll call delete and get rid of that cookie.
And again, in our completion handler, we will know that any future requests in the WebView will have that cookie removed.
And those are the basics of managing cookies using WKHTTPCookieStore.
We'll see a little bit more about that later but I'd like to move on to filtering unwanted content.
When you a load page in a WebView, an HTML document, there's all those sub resources that are loaded that I already mentioned.
And there's a number of reasons why you need to make on a resource by resource basis a decision about whether or not to load the resource or otherwise, change how the resource is loaded.
A common case we hear from developers is they're working on a browser targeted for schools, libraries, some other public place where the content loaded in the browser has to be family friendly.
So we need to filter out everything is not family friendly.
In a similar vane, we've heard from developers working on corporate intranet applications that they have varying needs within the same app.
One WebView might need to block all content that is not coming from the corporate's network.
Another WebView might need to block all content coming from certain specific servers.
You can have each WebView setup to do its own thing on a per sub resource basis.
And then there's insecure content.
We can upgrade it to be as secure as we possibly know how.
This can help keep our user's data and their browsing activity and our app's browsing activity hidden from prying eyes.
So the new WKContentRuleList API will help you do all of these things.
Now if you're familiar with Safari content blocking extensions where you provide a rule set to configure Safari to block certain resources loads, the ContentRuleList has the same syntax as those content blocker extensions.
With them, you can block specific resource loads by for example, matching the url of the resource to a regular expression.
You might be able to tell WebKit ahead of time don't ever load such urls.
In some cases, if you block a resource load all together, you can break the webpage in subtle, sometimes not so subtle ways.
So there is a rule action where you can tell WebKit to perform the load but make the resulting content invisible.
And then finally we can upgrade those insecure resource loads to be secure loads.
This comes in two important forms.
One is any http plain text url that is come across, you can upgrade it to be an encrypted https url.
And you can also block cookies from being sent out with certain requests.
If the requests are to a resource of a sensitive nature, you can prevent your user's data and session state from leaking out with those requests.
When you provide your rule list to WebKit, WebKit compiles into an efficient byte code format.
This is kind of an implementation detail that's not directly relevant to you.
I'm bringing it up because I want to assure you that a content rule list even a large set of thousands of rules we've been spending a lot of time working on making that as efficient as possible.
And no matter how big your rule set is, if it compiles successfully you should not see degradation in loading performance.
You supply your rules in a simple JSON format.
The JSON has pairs of triggers and actions.
In this example up on the screen, the trigger is matching a url that is a url.
It's for every single url that's a regular expression that says for all urls take the action of making the url https.
So this is a common and important shotgun approach to upgrading all plain text requests to be encrypted ones.
I'm not going to go into the other actions and triggers that are available right here because I have so much more I'd like to talk about today but there are resources that we will refer you to to get the full breadth of what is available to you.
Once you have your JSON, you'll pass it to the related new API, WKContentRuleListStore, to compile it into that efficient byte code format.
Now notice there's a string identifier passed in.
It's up to you to come up with what the string is and it will be relevant in just a little bit.
You'll have a completion handler and when your completion handler is called with the WKContentRuleList, you can create a WebView with that rule list installed and start loading content start loading web content that filters out unwanted sub resources.
So that identifier.
When we compile a rule list from JSON to the efficient byte code format, you can name it.
And then later you can look up by the same identifier so you don't have to compile it again.
WebKit stores it on the storage of the device and can look it up much quicker later.
So you can you only need to compile a certain rule set once.
Once you have your WKContentRuleList as I just mentioned, you add it to the WebView's configuration and now you're ready to go.
I believe there's no better way to see what cool new APIs can do other than a demo.
So I'd like to invite Alex on the stage to show you a little bit more.
[ Applause ]
I am making an app and it's an educational app.
And it will help people to learn something more about a subject that makes me very happy and that is dogs.
And so my users are going to read articles online.
They're going to take a quiz on these articles and then they are going to receive a diploma.
So since I am going to be showing web content and integrating into my app, I'm going to use WKWebView.
There we go.
We have a very simple app that basically does nothing but open a WKWebView and open up the web content in that WKWebView.
Let's see what it looks like right out of the box.
Okay. So this web service checks the http header for the presence of a cookie to indicate who the student is and if there is no cookie, then it will forward us to a log in screen.
And we could click here to log in.
And we could log in but this user experience can be improved upon and maybe we've already logged into the app using the touch ID or something.
And we know how to communicate with this web service.
The fact that this student is logged in with a cookie.
So we're going to do just that.
To do that, we need to make an http cookie.
We need to put this cookie into a website data store's httpCookieStore using the new API set cookie.
And then once that's done, we need to tell the WKWebView to use the website data store that has the cookie inside of it and we do so through the WKWebView configuration.
Set the website data store to be the one that has a cookie in it.
And then we proceed as we did before.
Use this configuration.
Open a WebView.
Open the web content.
So right off the bat, we are logged in.
Our initial request has this http cookie in its header.
So this article that I want my users to read has some sub resources that are loaded over unencrypted http connections.
And our app transport security, by default, keeps our users safe by preventing loads over unencrypted http connections.
And so right here, we see some missing images.
These the servers from which we wanted to get these images are correctly configured with https tls certificates on them and if we had made an encrypted https request, it would have responded with the same image.
So if we wanted to go in and take the http url and turn it into an https url just before the load, then we would receive the data that we wanted over a secure connection.
Let's do that using a WKContentRuleList.
Okay. I added a little bit of code.
Here we have the content rule list that we want to use.
In this case we have one rule.
It contains a trigger which is a regular expression that matches everything.
This regular expression is run on the urls of each resource WebKit is about to fetch.
The action is make https.
So if we would have requested an unsecure resource, request a secure resource basically by just adding an s into the scheme.
Once we have this JSON, we need to compile the content rule list.
Once this content rule list is compiled, we need to take the content rule list and add it to the WKWebView configuration through the user content controller and then proceed as before.
So let's see what happens when we do this.
We load the article and these resources are now being loaded over secure connections.
So we now have an app that starts with cookies and it promotes all of its insecure resources to secure resources.
Let's hear more about customized loading from Brady.
[ Applause ]
Thank you, Alex.
Pretty straight forward.
I do want to reiterate a couple of points.
Alex showed that you can now set a cookie in a WKWebView and know when the right time is to make a request where that cookie will be applied.
And that's enabled by the new WKHTTPCookieStore API.
And then to hammer it home, the one little bit of the content rule list power that we like focusing on is upgrading insecure, unencrypted resource requests to be https with just that simple rule list.
With one action and one trigger.
Alex showed how WebKit can efficiently do that automatically on your behalf.
And now I'd like to move on to our third and final new mechanism which is the ability to provide custom resources.
We've heard from a lot of developers that they have a bundle of web content that they control.
Some of them put it in their application bundle.
Some of them host it on a web server that they control.
And they would like to insert into that web content resource data local to the user's device.
One pretty cool app that has this need that we've seen is a photo book maker where it's using entirely web technologies to have a photo book layout where the company's designers are adding new templates for new layouts and styles of the photo books but they want to access photos that are local to the user's device.
And using this new API that will be possible.
There's also a lot of game developers out there we hear from who have an in game newsletter or an in game leaderboard that uses web technology.
And they want to integrate local experiences from their user's local play of the game so their user feels more attached to the goings on in the game's universe.
These are just a couple of examples.
There's many more things of what you can do with the new WKURLSchemeHandler API.
WKURLSchemeHandler allows your app to handle resource loads for a particularly url scheme.
Now just so we're all on the same page.
What is an url scheme?
These are examples of some urls we've probably seen.
And these are the schemes.
Everything that comes before the first colon.
So referencing custom url schemes that WebKit doesn't already handle itself, you can register a scheme handler to handle resource loads to any resource that has a url matching that scheme.
In this example, we've arbitrarily chosen the scheme local.
Not a standard url scheme that WebKit handles itself.
Not any part of any web standard or anything like that.
But one could conceivably see how it could become a web standard in the future.
It might become something WebKit handles itself.
So we'd like to encourage the best practice of future proofing your custom scheme by name spacing it with your company name or the app bundle name, something like that.
What is a WKURLSchemeHandler itself?
It's a fairly simple protocol that you implement with two methods so you are told to start loading a scheme task or stop loading one.
We'll get into what the scheme task is in a little bit.
Once you've implemented the protocol, you will set the scheme handler on the WebView's configuration and this is where you decide which url scheme you want that scheme handler to handle custom loads for.
You can use the same instance of your class for all the custom url schemes in your app.
You can have a different class for different url schemes.
You can have different instances for all the different schemes or have one shared instance for all the schemes.
It's up to you and your application's architecture.
And then you create your WebView and load some content in it.
And any sub resource in that web content that references your custom url scheme that you registered will call back into the object that you registered with the WKWebView.
And note that this includes even the top level html document itself, can be of the custom scheme that you registered.
So what is that WKURLSchemeTask.
Each task sent to your handler represents a specific sub resource load.
The task contains the url request object.
This is the same information that would go out to an http server if this request was going out onto the network.
It includes a lot of little bits of data about the resource but very importantly, the url itself.
And then the task has four straight forward methods for you to communicate loading progress back to WebKit for this particular resource.
Once you are given a task and told to start loading for it, first thing you need to do is create a response.
Just like that url request object represents what would go to a server on the network, this is the response that would come back from the server.
You need to manufacture that.
Very importantly, in the response, you need to include the mime type.
For those of you not yet intimately familiar with web technologies this is kind of like the file type.
It's you telling WebKit how to interpret the resource.
This is an HTML resource.
This is a JPEG image, etc. Once you have the response, you tell the task that the response was received.
In this case, we have all of the data for the response in memory already.
So we can immediately pass the data back to WebKit for the task.
And then we signal completion of the load to WebKit.
So WebKit isn't waiting for more data to come in.
Again, no better way to illustrate other than to see it into action.
So I'd like to invite Alex back on to stage.
[ Applause ]
So remember in my app, I'm going to have my users read articles online, take quizzes on those articles, and then receive a diploma.
And I have a team of web designers who is constantly coming up with beautiful diploma designs.
And so I want the users of my app to go and fetch these new designs from my servers but I want each diploma to include a picture of the user's avatar and I want that to be loaded locally on the user's device.
And so to do this custom local loading within WebKit, I am going to use a custom scheme.
Now let me show you the source of my diploma.
Okay. So here I have some HTML.
And I have an image.
And its source has a url with a custom scheme.
In this case, canine school dash avatar.
And if I open this in a browser or in a WKWebView the WKWebView won't know what to do with this.
Let me show you what that looks like really quick.
Take the quiz.
Okay. So here I have my beautiful diploma.
And I have a missing image tag because WebKit tried to load a resource that had the school that had the scheme canine school dash avatar and WebKit doesn't know what to do with such a scheme.
So we can use the WKURLSchemeHandler to teach this WKWebView what to do with such a request.
Okay. So I added a bit of code.
I have an object, a class that implements WKURLSchemeHandler and there's a bit of code.
I'll get back to this in a second.
But down here, in my WKWebView configuration, I need to call the new method set URLSchemeHandler with an instance of my class that implements url, WKURLSchemeHandler and then I need to tell WebKit what url scheme this class is going to handle the requests of.
In my class implementation, implementing WKURLSchemeHandler means I have these two methods.
Start and stop.
Start is called by the web content when a request for this scheme is generated.
So in this case, when the web content wants to load my image.
It's my job to respond to this request using this url scheme task.
In my case, I'm going to pull up an image picker and allow the user to pick an image.
So I don't need to respond immediately.
If the WKWebView calls my stop method while this happening, then it I shouldn't respond to that request anymore because it was cancelled or something like that.
Down here once my image picker is done, I will have my data.
In this case, I will JPEG encode it.
And I need to call these three methods.
Did receive response.
Did receive data and did finish.
Did receive data is special.
I can call it more than once as the data is generated or as its received or however I want to provide this data.
In this case, I'm only calling it once because I have the entire data of the response, the JPEG encoded image in one buffer.
So let's see what happens when I have this implemented.
Okay. I'm already logged in because I have the cookie.
Everything is loaded securely.
I take the quiz.
Okay. So this image picker comes up because this start method has been called.
I go into my camera roll and I choose my avatar photo that I want to be in my web content.
And then because of calling those methods on the task, I now have that data in my web content.
So I now have an application that manages its cookies, that uses a content rule list to upgrade insecure requests, and that does custom loading through a WKURLSchemeHandler.
Back to Brady.
[ Applause ]
So to reiterate once again, what we just saw, Alex show us in code.
He chose a future proof url scheme.
A best practice that I want to reiterate a few times to make sure you don't choose a scheme that might become some sort of standardized schema in the future.
And then he showed something I hadn't even explicitly mentioned which is that you can provide the data for a task asynchronously.
You might generate the data locally.
You might have the user take a picture.
You might do your own networking to get the data.
However long it takes for you to get the data you can provide it back to WebKit asynchronously.
So that is it for all the new stuff I have to talk you about today.
Today we went over WKHTTPCookieStore, the new API for managing cookies in your WKWebViews.
We talked about WKContentRuleList, a fast and efficient way to filter unwanted content from your WKWebViews.
And we talked about WKURLSchemeHandler, a fantastic way for you to provide custom resources to web content no matter where that web content comes from.
I'd like to reiterate one more time that we came up with these new APIs and worked hard on them because of developer feedback that we've gotten from all of you.
And we'd like you to continue giving us your feedback so we can gather it and come up with the next great new features that will exist in future versions of WKWebView.
For more information, please visit the website for this session and there are some related sessions I'd encourage you to look into.
Tomorrow morning we're going to talk about some great new things in Safari View Controller.
So if you are interested in adding a powerful in app web browsing experience to your application, please come see that.
And we also have well some old talks about when we first introduced the WKWebView API.
And also when we introduced those content blocking extensions for Safari.
That's where you can get a lot of detail on the powerful rule set that is available to you using WKContentRuleList.
Thanks for coming and I hope you have a great rest of WWDC.
[ Applause ]