Transcript
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
>> Good afternoon, everyone.
My name is Anders, I am
an Engineer on the Safari
and WebKit Team, and
I'll be joined later
by my colleague Beth.
And this session is Session 206,
Introducing the Modern
WebKit API.
We've got a lot of
really cool things
to show you, so let's
get started.
So first, I think I want to
give you a brief overview
of what WebKit actually is.
One way you can think of it
is that WebKit is the layout
and rendering engine behind
Safari on Mac and iOS.
So WebKit parses and
renders HTML, it loads
and displays images, it runs
JavaScript, and so forth.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Another way to look at
it is that WebKit starts
where the Safari
user interface ends.
But WebKit is also used by a
lot of other apps, such as mail
for displaying rich HTML email.
And iBooks for rendering
gorgeous ebooks.
And even applications you
wouldn't normally think were
using WebKit, like messages
for its conversation view.
And of course most
important of all, your apps.
You've done some really
amazing things with WebKit.
So if you're using WebKit
in your app today on iOS,
you're using UIWebView.
So let's have a quick
show of hands,
how many of you are using
UIWebView in an app today?
Wow. On iOS X, you will
be using a WebView.
And how many of you
are using a WebView?
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And how many of you
are using a WebView?
Cool. Now, over the years,
we've received a lot of requests
from you especially on iOS for
something a bit more powerful.
And we really think we have
something that you're going
to like, and that is
the modern WebKit API.
So what are you going
to learn today?
You'll see how to adopt
the modern WebKit API
and to use it in your apps.
We'll take a look at few
new cool features and how
to integrate those in your app.
And then we'll dive a bit deeper
and you can see how you can
customize the web WebKit loads
web pages, and even customize
the web content itself.
So the modern WebKit API.
Class for class, this
API is exactly the same
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Class for class, this
API is exactly the same
on iOS and OS X.
Yeah, that's pretty cool.
[ Applause ]
Of course there are some minor
changes due to the way we hook
into AppKit and UIKit, but
it's pretty much the same.
We're also taking advantage of
the latest and greatest language
and cocoa features, so you can
spend less time writing glue.
code and more time focusing
on making your app great.
And we've tried really hard
to reduce the surface area
while still giving you access
to a ton of cool features.
And we're using the
multi-process architecture
that we're also using on
Safari on both OS X and Lion,
and now on iOS with iOS 8.
In fact, one of the goals we
had with this modern API was
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
In fact, one of the goals we
had with this modern API was
to take a lot of features that
were previously only available
to Safari and give
you access to them.
For example, super smooth 60
frames per second scrolling
using hardware acceleration
and Core Animation.
The full power of the
JavaScript Nitro engine
on both OS X and iOS.
[ Applause ]
And this includes the
fourth-tier compiler,
that Greg was telling
you about yesterday.
We've taken the back/forward
swipes and the pinch
to zoom gestures and from Safari
and built them right
into WebKit.
And we have a really cool
way for your app to talk
to web pages and vice versa.
Now, I mentioned that
WebKit is multi-processed.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now, I mentioned that
WebKit is multi-processed.
And what does this really mean?
It means the web content runs
in a separate process completely
isolated from your app.
Like this.
Now, why is this a good idea?
Well, it's great
for responsiveness.
Let's say you have
loaded a web page
and the web page is running a
lot of scripts and doing a lot
of layout, your app will
still stay responsive even
if this happens.
And if you've loaded a web
page that is creating a lot
of JavaScript objects and using
a lot of memory, we'll make sure
to handle that gracefully
instead
of having your app
be jettisoned.
It's also really good for
battery life and power usage.
Since each web page runs
separately in its own process,
we can put individual web pages
into a low power mode for things
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
we can put individual web pages
into a low power mode for things
like background tabs and
fully-occluded Windows.
Now, this is also
completely transparent to you,
when you create a WKWebView,
that is our new WebView class
in the modern API, we'll spin
up a web content process.
When you create another
WKWebView,
we'll spin up another
web content process.
Now, we do this up
to a limit and then
when you create more WKWebViews,
they'll share a web
content process.
And when you deallocate
your WKWebViews,
we tear down the processes
for you and you don't have
to worry about a thing.
So let's talk about how to
adopt the modern WebKit API.
So Beth and I are both
huge Wikipedia fans.
We love reading Wikipedia,
we read it all the time,
and so we figured it would
be a kind of cool idea
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and so we figured it would
be a kind of cool idea
to write a dedicated
Wikipedia browser app using the
modern API.
This is what it looks like.
It's called WKPedia.
Now, if you think this is just
a WKWebView inside a [inaudible]
Window, you're absolutely right.
But don't worry, we're going to
add some more features to it.
But how did we create
this WKWebView
and how did we load
the web page?
Well, creating a
WKWebView is really easy.
Just as with any other view, you
call alloc and then you call it
into a frame, and that's it.
And how do you load a web page?
You get the URL, you
get the URL request
and then you call
WKWebView load request,
and that'll load the
web page for you.
So that's all good if you have
an app with a single WebView.
But for WKPedia, we really want
to have support multiple windows
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But for WKPedia, we really want
to have support multiple windows
so you can have more than
one article showing at once.
And when you do this, there's
always some state that you want
to share between your web
views, such as preferences,
the set of processes to
create your web pages in,
and this set of links.
And the way to do this is
to create a single
configuration object and then
when you create your WKWebViews,
you pass along the configuration
and that'll ensure that all
the state is correctly shared
between the web use.
This is what it looks
like in code.
First, you create your
WKWebView configuration
and then you create a WKWebView,
but you use an input
frame configuration
and you give it a configuration,
and that's all you have to do.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and you give it a configuration,
and that's all you have to do.
Okay, back to WKPedia.
Now, even though
this is not going
to be a full-fledged browser,
there's still some browser-like
features that we want to add,
such as a back/forward button, a
title and a progress indicator.
So let's take a look at
how we would do this.
And let's go from left
to right and start
with the back/forward buttons.
For this, we have a bunch of
actions methods on WKWebView.
Here are some of them.
You can hook these up
in interface builder
to your UI elements without
having to write any code.
And so for WKPedia, the
ones we're interested
in are go back and go forward.
And these are also
auto-validating,
which means that when you have
hooked these up to your UI,
it will actually update the
enable/disable state based
on whether you can
go back or forwards.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
on whether you can
go back or forwards.
So you don't have
to worry about that.
So what about the title
and the progress indicator?
For that we have a bunch
of properties on WKWebView.
Here are some of them.
These are Cocoa key value
observing compliant,
which means that you can use
the normal Cocoa KDO methods
to listen for any changes
to the property values.
And if you're on OS X, you can
use Cocoa bindings to hook them
up to your UI without having
to write a single line of code.
So for WKPedia, we're
interested in the page title,
just the title property, and
whether the page is loading
or not, and that's
the loading property.
Okay, so WKPedia is really
starting to come along here,
but it's still not a
dedicated Wikipedia browser.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
but it's still not a
dedicated Wikipedia browser.
Now, what do I mean by that?
Well, let's say you go to the
WebKit article in Wikipedia
and you click this link
to the WebKit homepage.
That'll open the link
inside the WKPedia browser,
but we want this to be a
dedicated Wikipedia app
and we don't want to
load any external links.
So what we want to do is
customize the way pages
are loaded.
But before I can
tell you about that,
I need to explain how page
loading actually works
in WebKit.
So first, something happens
that triggers a page load,
this can be one of many things.
For example, the
user clicking a link
or using the back/forward
buttons,
it could be JavaScript setting
the window docile location
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
it could be JavaScript setting
the window docile location
property, or it can
be a subframe loading,
because subframes and frames
are just documents inside
of each other.
And as we saw earlier, it
could be you calling WKWebView
load request.
Then we send a request
off to the server
and we get back a response.
This could be a positive
response
or the server could send back
a 404, which means, sorry,
I don't know what this
resource is, file not found.
And then, the server sends
back some data and we're done.
Now, what you can do with
WebKit is to have your app sort
of inject itself after the
action and response phases.
And to decide whether
to continue the load
or whether to cancel it.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
or whether to cancel it.
And the way you do this
is by implementing methods
on the WKWebView
navigation delegate.
There are 2 methods
that correspond
to the 2 different phases.
The first one is decide policy
for navigation action,
for the action phase.
The second one is decide
policy for navigation response,
for the response phase.
And with both of these delegate
methods, you get data objects
that contain enough
information for you to be able
to make an informed decision
about whether you want
to continue the load or not.
So for decide policy
for navigation action,
that object is a WK
navigation action object.
It has properties.
For example, the navigation type
lets you find out which type
of navigation actually
started the load,
like if it was a link being
clicked or if it was a go back,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
like if it was a link being
clicked or if it was a go back,
go forward request,
and so forth.
Request is the request
that we're going
to send to the server.
And with the modifier flags
property, you can even tell
if the user held down
shift or command or option
when clicking the link.
For decide policy for
navigation response,
you get a WK navigation
response object.
This also has some properties,
response is the HTTP.
response that we get
back from the server.
And in addition to
these data objects,
you also get a decision
handler in the form of a block.
This is how your app can
decide whether you want to go
through with a load
or cancel it.
And you do this by calling
the block with 1 of 2 values.
So for decide policy
for navigation action,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
you either pass WK navigation
action policy cancel or allow.
And for decide policy
for navigation response,
you pass either WK navigation
response policy cancel or allow.
Now, you can either call
these blocks right away
or you can call them
sometime later,
which can be really useful
if you want to put up some UI
and let the user decide whether
the load should continue or not.
So now I'd like to ask my
colleague Beth up on stage
to show WKPedia and how to
add some of these features.
Beth.
[ Applause ]
>> Hi everyone, I'm Beth Dakin.
Anders and I work together
on Safari and WebKit,
but lately we've been working
on our pet project, WKPedia.
So I have a super bear-bones
version of the app to show you,
and together we'll use
all of the information
that Anders just gave us
to build it up and turn it
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that Anders just gave us
to build it up and turn it
into a dedicated browsing app.
So I'll show you what we have
and then we'll work
on improving it.
Just going to build and run.
Here we go, this is
WKPedia as it stands.
It's really just a
WKWebView in a window,
we get all of the great
WKWebView features,
super fast scrolling, we get
the super fast JavaScript engine
with the fourth-tier
compiler, can click on links,
navigate to new pages.
So that's great but I'm
clearly missing some basic
browsing features.
If I look at the same website
in Safari, for example,
if I click on a new link, then
I get some progress indication
at the top, indicating
how the load's going.
The URL bar gives me some
indication of what page I'm on.
I can go back.
These are things that we
clearly want in our app for it
to feel like a web browser.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to feel like a web browser.
So let's add them.
I'll give you a quick
tour of the code first.
This is our main class, it's
a browser window controller,
and that's an
NSWindowController.
And if we look at the
implementation file,
you'll see this is where we have
a property for the WKWebView.
And down here in
window did load,
this is where we allocate
our WebView and it's
where we load our
initial request.
Okay, but we want to
add some toolbar items,
so let's go into
interface builder.
I need to get my toolbar
here, there we go.
First I want to make the
toolbar visible at launch,
and then we'll double click
it to start adding some items.
So first we wanted a
back and forward button,
so I will drag those in.
And if we go over to this panel
here, let me highlight one
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And if we go over to this panel
here, let me highlight one
of these buttons, here
I'll zoom this up for you.
You can see I've already
hooked these buttons
up to the appropriate method, so
the back button's already hooked
up to the go back action method,
and the forward button's
already hooked
up to go forward, so
those should work.
Then we want some indication
of the page that we're on.
So we don't want our URL field
here, because we want this
to be a dedicated browser app,
we don't want people to type
in random URL's, they'll use
this search field and Wikipedia
to get to different pages.
So we just want some texts, but
we want it to reflect the title
of the Wikipedia page
that we're actually on.
So that's very easy.
Over here let me
zoom this up again.
So for this text, we just want
to bind it to the file's owner,
which is that browser
window controller class
that I showed you a minute ago.
And then we want to
set the model key path
to WebView dot title.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to WebView dot title.
And so that's one of those
KVO-compliant properties
on WKWebView that Anders
was telling us about.
So this should be all
that we have to do,
this should actually update
whenever the title value
changes, just get all
of that KVO goodness.
All right, and finally, we just
want some progress indication.
So I'll drag in the spinner too.
Okay, that looks about right.
So let's see how that
works, let's build and run.
Okay, so we have
some progress here.
I'll zoom up a little.
We have back/forward
buttons, great,
we'll see if they
work in a second.
We have a title that
reflects the page
that we're on, San Francisco.
Our spinner doesn't seem to be
doing quite the right thing,
it's just kind of always there,
and it's not indicating
any load progress yet.
I navigate to a new page, great,
the title updated right
away to reflect San Diego.
Click my back button, and
we went back, awesome.
Forward, great.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Forward, great.
Okay, so we've made a
lot of improvements.
We need to fix that spinner.
I also feel like this title is
really long and unnecessary.
They all seem to have
this suffix, Wikipedia,
the free encyclopedia.
I think it would be
more useful in our app
if we truncate the title down
just to be the beginning part.
So we can handle
that very easily
in interface builder as well.
So let's make those two fixes.
First let's fix that spinner.
To fix that we want to go
over here again to this panel
to the animation section,
and we want to bind
to the file's owner again.
Again, that's our browser
window controller class.
And here the model key path
we WebView dot loading, yes.
And again, that's one of those
KVO-compliant properties.
That should be all we
have to do to get this
to animate whenever
WebView dot loading is true.
There's one other thing I
want to do with this though,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
There's one other thing I
want to do with this though,
which is over here, I want to
uncheck display when stopped.
So then it should go away
when it's not animating.
Okay, and now we want to
truncate that title also.
So I did already write some
code that would do that.
Down here if we look in here,
so this is just some simple code
that I wrote that would take an
NS string and see if it had this
as a suffix at the end,
and then return a string
that doesn't have that part.
So I already wrote some code
to do that and I can invoke it
from within interface
builder right
over here using the
value transformer option.
Delete Wikipedia title
snippet value transformer.
So that will take the title
and invoke this method
with the title and then the
result will be what's reflected
in the toolbar.
So let's save, build
and run again.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So let's save, build
and run again.
Okay, so progress has been made.
Let's try clicking another link.
We get our spinner.
It's animating.
It goes away when we're gone.
Our title is now a much smaller
more useful thing for our app.
We still have our
back/forward buttons.
This is looking great, this is
really feeling like a browser.
But I haven't done anything
yet to make it a dedicated
Wikipedia browser besides just
not providing a URL field.
But if I go to a link like
this, and click on it,
it's going to still
open right in my app,
which is not the behavior
that I'm looking for that sort
of defeats the purpose of this
being a dedicated Wikipedia
browsing app.
So we can easily fix this
by implementing the
navigation delegate just
like Anders taught us.
So let's do that right now.
Let's go back to the code.
Okay, so first, I want
to add 1 line of code
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Okay, so first, I want
to add 1 line of code
to window did load here, to
set the navigation delegate.
Here I'm setting the
navigation delegate to self,
so this way WebKit will know
to look here in my class to see
if there's any implementations
of these delegate methods.
And then we want to implement
1 of the 2 delegate methods.
We want to implement decide
policy for navigation action.
So let's quickly
step through this.
So first I want to get
the URL that the app wants
to navigate to, and then
if the host URL does not
have a Wikipedia.org suffix,
so this is a non-Wikipedia
link, then in that case,
I want to pass the
navigation off
to the default browser
on the system.
So that's what will happen
with this line of code.
Safari should open
that link instead.
But we still need to
tell our WKWebView
that it shouldn't go ahead
with the navigation also,
so we'll invoke the
decision handler block
with the WK navigation
action policy cancel value.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
with the WK navigation
action policy cancel value.
And while we were here I
thought it would be cool just
to add a feature to command
click a link and open it
in a new window in my own app.
So that's what I
added down here.
So first I'm finding out if
this navigation is happening
with the command key down,
and if it is, then I'm going
to create a brand new browser
window controller and I'm giving
that this navigation request.
And then again, since I
don't want this WKWebView
to also navigate
to the new page,
I invoke the decision handler
block with the cancel value.
And otherwise if we didn't fall
into any of those situations,
then we'll invoke the decision
handler block with allow.
So let's save and build and run.
Okay, great, so regular
page loading still works,
so that's good.
Let's see, let's click on
this link, and awesome,
we successfully handed
that navigation off
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
we successfully handed
that navigation off
to the default browser.
And if I command click a link,
I get it in a new
window in my own app.
Each of these are
in its own process
because this is modern
WebKit API, pretty cool.
So this is awesome, in just
a few minutes, we have built
up a dedicated browser app
using the modern WebKit API.
But I still feel like there
are few modern features
that I'm lacking here.
For example, again, if
we look back in Safari,
who really uses the back
button anymore, I mean,
I typically use the swipe
back gesture to go back.
And I also want to
be able to double tap
to zoom and pinch out.
If I try to do those things
in my app, nothing happens.
Double tap, no, nothing's
happening.
But it's really, really
easy to add these features.
So I'm going to do
it really quickly,
even though Anders hasn't shown
us how to do it quite yet.
But we just need to
add 2 lines of code.
Back up here in window did load.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Back up here in window did load.
Okay, so this first line of code
allows back/forward navigation
gestures, set that to yes,
that gets you the
back/forward swiping.
And allows magnification,
that will get double
tap and pinch to zoom.
Save that, build and run.
Okay, so now if I go
forward, should be able
to swipe back, awesome.
Can double tap, pinch
right out, awesome.
So there you have it,
we've built a modern --
we've used the modern WebKit API
to build a dedicated
Wikipedia browser app
with modern filling features
in just a few minutes.
So I'm going to hand
it back to Anders,
he's going to teach you a lot
of the more advanced features
that you can do with this API.
And I'll be back up later
to show you how you can
integrate some of those features
into our iPad version
of WKPedia.
Back to you, Anders.
[ Applause ]
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Applause ]
>> Thanks, Beth.
That was really cool.
I especially like the
extra touch at the end
where you added adjuster
support.
Let's recap how Beth did that.
So for the navigation gestures,
that's the swipe to go back
and forward, you just set
the allow the stop forward
navigation gestures property
to yes on your WKWebView.
For zoom gestures on OS X,
you set the allow
magnification property to yes.
On iOS, this is handled by
the underlying UI scroll view,
and it's already on by default,
so you don't have to do a thing.
So let's talk about
WKPedia for iPad.
This is what it looks like.
It's really great, I
use it all the time
for reading Wikipedia
articles on my way to work.
But I've noticed something
that bothers me a little.
See, I like to read certain
sections of an article,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
See, I like to read certain
sections of an article,
and that means that I have to
go find the table of contents,
I have to scroll down to it.
I find a section that I want
to read, I tap on that link.
And then when I've read it, I
need to scroll all the way back
up or swipe back and look for
another section and read that.
So I told Beth about
this and she said, well,
why don't we use some of
the more advanced features
of the modern WebKit API
to take care of this?
Like why don't we just
get rid of the table
of contents from the web page?
And while we're at it, we can
also get rid of this side bar
to the left, to make
room for the article.
And then we can put the table
of contents natively in the UI,
so we have this little button
in the top left corner,
and when you tap it, you
get the table of contents
in a native UI table view.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And when you're in
landscape mode,
you can always have it visible
because of the extra space.
So what we really want
to do here is customize web
page contents from a Wikipedia.
And we want to use two
features to do this.
One is called user scripts.
The other one is
called script messages.
These are both handled
by an object called WK
user content controller.
WK user content controller
is part of the configuration.
So it's a property
on the WKWebView configuration
called user content controller.
So let's talk about
user scripts.
User scripts are a way
for you, app developers,
to take some JavaScript and
inject it into the web page,
and then have it do something.
This will happen automatically
for every web page.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
This will happen automatically
for every web page.
So you don't have to manually
call it every time the
page loads.
So when you add user scripts,
you need to consider two things.
The first one is when you
want your user script to run.
You can either run your user
script at document start time.
This is right after the document
element has been created
but before any other
document has been parsed.
Or you can run them
at document end,
which is after the
document is finished parsing
but before any subresources
such as images have
necessarily finished loading.
This corresponds to the
down content loading
event, load event.
You also want to consider
where these scripts should run.
You can run them either for all
frames, so both the mainframe
and the subframes, or just
for the mainframe only.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and the subframes, or just
for the mainframe only.
So how do you create
a user script?
First, you get the script
string from somewhere.
In my case, I just have it as
a string literal in my code.
Then you create a WK user script
object and pass the source code
and here I've highlighted
the when and where.
So we want this user script
to run at document start time
but only for the mainframe.
And then you tell the user
content controller to go ahead
and add the user
scripts, and that's it.
Now, what can user scripts do?
User scripts can do
anything that scripts running
on the web page can do.
So that includes
using the down API
to change the document
structures.
Adding event listeners for
onload events or click events
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Adding event listeners for
onload events or click events
or keyboard events or any event.
You can load external
resources like images
and even XML-HTTP requests.
And you can also communicate
back to the application,
and this is where
script messages come in.
So script messages are messages
that user scripts send
to the application.
They consist of JSON
data, so objects,
arrays, strings, etcetera.
When you send them
from your web page,
we automatically convert them
to the corresponding
objective C-types.
So N as dictionaries, N as
arrays, N as strings, etcetera.
So when you want to listen for
script messages in your app,
you want to register a
script message handler.
This involves creating
an object that conforms
to the WK script
message handler protocol.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to the WK script
message handler protocol.
This protocol has 1 method
that you have to implement
that receives script message.
And then you tell the
user content controller
to add the script message
handler and you give it a name.
And this name is
really important
because it is how your web
page communicates back to you.
So when you've added a
script message handler,
we express it window dot WebKit
dot message handlers dot your
name dot post message function.
Here's how you use this
post message function.
You get your message in
JSON form from somewhere,
and then you call post message
and you pass along the message.
So on the receiving side,
WebKit will call it
receives script message
on the correct user
script message handler,
and the body property of
the WK script message object
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and the body property of
the WK script message object
that is passed along here
will have your message
automatically converted.
There are also some other
properties in WK script message,
you can find out which web page
posted the message, and the name
that was used to
post the message.
One interesting thing
about script messages is
that it's just not user scripts
that can send script messages.
Your web page can
also send messages.
This is really cool if you
have a website and an app
and you want the
website to be able
to communicate with your app.
But it also means that if
you have a generic browser
that can load any
website, you don't want
to blindly trust messages that
are posted by these websites.
So make sure you do the
right message validation
and then check that
the object is
of the expected types
and so forth.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of the expected types
and so forth.
So now I'd like to ask Beth
to come up on stage again
and show off WKPedia for iPad.
Beth.
[ Applause ]
>> Thanks, Anders.
So I'll show you
WKPedia for iPad
and then we'll add
some user scripts
and some script messaging to
add these cool new features.
Okay, so back into Xcode, so
I'm using the same Xcode project
for both versions
of WKPedia actually.
So I'm just going to close up
the OS X version, open iOS code,
and switch my scheme to the iOS
scheme, so that now when I build
and run, I'll be building and
running for the iPad simulator.
All right, so let me
show you what we have.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
All right, here's
WKPedia for the iPad.
So again, our idea that we
want to use user scripts
for is we want to hide
some of this content,
we're going to hide
this side bar
so that we can maximize
the screen space
on this smaller screen,
and we also want
to hide this table
of contents section.
And then we want to extract
all of the data from the table
of contents section and
put it into a UI table view
that we already have in place.
So we can do this
with two user scripts.
First we will add a user
script to hide the things
on the page that we want hidden.
And then we'll go back and fill
in our UI table view
with another one.
All right, so first I'm going
to add a JavaScript
file to my project.
So I have one here
called hide dot js.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So this is a really
simple JavaScript file.
First I'm creating
a style element
and I'm appending it
into the document.
And the style element, I'm
giving it just three styles.
So we used the Safari
Web Inspector to look
at what the Wikipedia
code looks like right now,
and we saw that the table
of contents that we want
to hide has a class name of TOC.
So I'm adding another
style to that class.
I'm setting it to display none,
and I'm making that important
so that it will override any
other display styles they have
set on it.
And the side panel has an ID
of NW panel, so I'm setting
that to display none as well.
And finally, I just want to
adjust the margin on the content
to make sure it fills
in that extra area now
that the side panel's gone.
Okay, so this is the JavaScript
that I want my app to invoke.
So now we just need to add
some code to invoke it.
So WebView controller,
this is my main class
in my iPad version.
You'll see this is where
again we have a property
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You'll see this is where
again we have a property
for our WKWebView should
look awfully familiar.
So here I have a currently empty
method called add user scripts
to content controller.
So you can see this is
invoked from load view.
So and then it's passing in
the WK user content controller,
which is a part of
the configuration.
So Anders mentioned the
configuration earlier
on in the talk.
So user scripts are an example
of something that's part
of the configuration.
So that's an example of why you
want your different WKWebViews
to share a configuration, so
they all get the user scripts.
All right, so let's
invoke this script.
It's just three lines of code,
so first we're creating
an N S string
with the contents
of URL hide dot js.
So we're going to take that file
that I just added to the project
and turn it into an N S string.
Then I'm creating a WK user
script object with that string.
I'm giving it an injection time
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of WK script injection
time at document start.
So this means this
script will be injected
and it will run right after
the document element is created
before anything else happens.
That's good, that's what I
want to happen for this script,
because I want to make
sure those styles are there
right away.
I don't want the chance
for the table of contents
and the side bar to appear and
only then does my script run,
so then they disappear.
That would be terrible.
So document start,
that's what I want.
And yeah, for the
mainframe only.
Okay, then finally I just
need to add the user script
to the user content controller,
and that should be
all I have to do.
So let's build and run.
And success, the side panel is
gone, it's not longer there.
We just have the article taking
up the full width of the page.
And we also have hidden the
table of contents section,
we go right into
the history section.
Awesome. So now let's
add a second user script
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Awesome. So now let's
add a second user script
to get the data from that
table of contents section,
put it into our UI table view.
So first we'll add a second
JavaScript file to our project.
So this goes through to that
table of contents section,
it bundles up all the data we
want, puts it into an array,
and then it sends it -- this
is the important part --
it sends it as a
message back to my app.
So let's make sure
this code gets invoked,
and then I'll show
you the code that runs
when the JavaScript
sends the message back.
So back here in my
add user scripts
to content controller method,
I'll add a little bit more code.
Okay, I'm creating another N S
string with the contents of URL.
This time it's with
my fetch dot js class.
So we're putting that
JavaScript into an NS string,
then creating a WK user
script with that string.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
then creating a WK user
script with that string.
And this one I want
to be injected
at the document end time.
So this time I want to make sure
that the DOM has been built up,
that it has all of those
elements representing the table
of contents in it
before my script runs.
Otherwise, it's not
going to find anything.
And again, for the
mainframe only,
I'll add that as a user script
and then this is an important
line of code I need to call this
so the JavaScript can
call back into my app.
So add script message
handler, set it to self,
and give it a name, did
fetch table of contents.
So when the JavaScript
calls back into my app,
I'll make this a little
wider, this is the method
that will be called, did
receive script message.
So what I'm doing in that
method is I'm making sure
that this is the script that
I'm expecting to be calling,
it did fetch table of contents.
And if so, I'm calling
into my UI table view class
with a method that
I've written there.
So I'll show that to you.
So here in my UI table
view code, I'm just going
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So here in my UI table
view code, I'm just going
to make sure that this
message body this is the data
that was sent to me
straight from the JavaScript,
making sure it's an array
because that's what
I'm expecting.
Then I call make entries, which
is implemented right up here.
And I go through that array and
I make sure it's all the data
that I'm expecting it to be,
that I've written my
script to send over.
It's a string representing
the title.
It's the URL representing
the URL
that that title would
navigate to so
that we can have our
app navigate to that URL
when the user taps on it.
And if I have all of that
data like I'm expecting it,
I create a table of contents
entry and I add it to an array.
And then finally back here this
is where I invoked that method,
I'm going to tell the table view
to reload its data
at this point.
And then that data should
all be in there, okay.
So set this all up,
let's see if it works.
Let's build and run.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So here we are, WKPedia, every
thing's hidden still, great.
Contents did not
quite work, all right.
So I suspect that I have
a bug in my JavaScript,
and one thing that's
really cool is
that I can actually inspect
all of the web content
in my WKWebView using
the Safari web inspector.
I can even inspect
the user scripts
that I've injected,
which is pretty cool.
But don't worry, only you
can do this with your app,
other people won't be
able to inspect your app.
So let me open the inspector.
Let's make sure this
happens on all pages.
Go to a new page, still no
table of contents, okay.
So.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Here, if I go over into
my resources panel,
here let me zoom
this in for actually.
You can see that I have
my user scripts over here
under the extra scripts section.
So under user script 2, this
is the script I want to look
at so I can add a
breakpoint there
and just reload right
in the inspector.
All right, let's
get rid of that.
Okay, let's step through
to OC links, all right.
Do we get into the loop?
We do. Great, we get the
texts, not a text break.
Okay, well, there's the bug,
we had 2 breaks in a row
so we never got to the
code that added any
of this data to an array.
So this should be an easy bug
to fix now that I've spotted it.
So let's go back into Xcode.
There she is, all right,
let's build and run.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Yes, we have our
table of contents.
So now I have our
table of contents.
If I click around here, I'll
navigate to the right section
of the document because I
was sure to include that data
when I passed it
back from the script.
And there you have it.
So today we've built a
dedicated Wikipedia browser app
for Mac and for iOS.
We've added some
advanced features
to make our app interact
with our web content.
And we've done it all with
the modern WebKit API.
So that's all I have for you,
I'll hand it back to Anders now.
>> Thanks, Beth.
Wow, this is really going to
make my Wikipedia browsing
so much more efficient
on my way to work.
So the Modern WebKit
API available for iOS 8
and OS X Yosemite same
API on both platforms.
You get the full
JavaScript Nitro engine,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You get the full
JavaScript Nitro engine,
including the fourth
tier compiler.
It's multi-process, which
is great for responsiveness
and also for battery life.
And with user script and script
messages, web pages can talk
to your app and vice versa.
Now, we've only shown a
handful of classes here,
but the Modern WebKit API
has much more to offer.
So here's what I want you to
do, adopt the Modern WebKit API.
If you're thinking
of writing a new app,
I would strongly consider
using the new API.
If you already have an app
that is using either UIWebView
or WebView, don't worry,
those API's are still there.
But we are committed to this
new API, the Modern WebKit API,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But we are committed to this
new API, the Modern WebKit API,
and some features, like faster
JavaScript are only available
in the new API.
And since this is a new
API, we would really love
to hear what you think about it,
so buy radars, come to the labs,
bring your code, post
in the developer forums.
If you want to contact our
frameworks evangelism team,
you can do so at
this email address.
There's more information
on WebKit and Safari
on the Safari Dev Center.
And the Modern WebKit
API is part
of the WebKit Open Source
Project, so you can go
to WebKit.org and check it out.
You can also go to the developer
forums and post your questions
and we'll make sure
to answer them.
Related sessions, if you have an
app and a website and you want
to make sure they work together,
there's a great session this
afternoon called Your App,
Your Website, and Safari.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
On Thursday, there's a session
about the web inspector.
And on Friday morning, there's
a really interesting session
where engineers on the iWork
team talk about best practices
for sharing code
between iOS and OS X.
And thank you very
much for coming.
Thanks