WWDC2017 Session 220

Transcript

[ 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.
We load the web content and
render it and execute
JavaScript, that type of stuff,
in a separate process from your
application's process.
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.
Web content can be run
concurrently with your
application because of the
security, we can enable the
advanced JavaScript Just-In-Time
compiler.
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.
Images, JavaScript files, style
sheets, things like that.
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.
You know if you're a veteran
WKWebView user, you might have
used JavaScript to get the
cookies visible to the current
document displayed in a WebView
but when you do that, you didn't
get at the http only cookies
that are supposed to be hidden
from JavaScript.
Now that is possible.
And then finally it adds a
mechanism for you to observe
changes to the cookie store.
While you might be specifically
adding or removing cookies from
the cookie store, any resource
requests that goes to a server
and comes back could also add
new cookies to the store or
JavaScript executed in a page
could add new cookies.
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 ]
>> Thanks Brady.
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.
Right here.
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.
All right.
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.
All right.
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 ]
>> Thanks Brady.
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.
Dogs. Yes.
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.
Dogs. Yes.
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 ]