[ Music ]
Welcome to optimizing web content in your app.
My name's Jonathan Davis.
I'm the web technology's evangelist
for Safari and WebKit.
Now let me get something out of the way up-front.
I've had people tell me I look like Edward Snowden,
but I promise you I am not him,
but with all the satellites flying overhead,
they may be all out to get us.
That aside, I'm really excited to show you some new things
that will help you get more performance
out of web content in your app.
And we've all known for a long time that performance is key
to a great user experience.
So if you're an app developer that uses WebViews
and JSContext in app today.
And you care about performance, you're in the right place.
So we're talking about performance.
And in this day and age, what we really mean
by performance is battery life.
I mean battery life is the ultimate limited resource.
It can be the difference in making that one last phone call
or sending that last critical file before the battery dies.
And performance really matters to our users,
and they naturally choose apps that don't slow them down
and don't drain their battery.
So what's we've all learned together, from users' feedback
and from each other, is that performance really matters
because battery life matters.
So this year we focused on giving you better tools to find
and fix performance issues in your web content.
Now we have tools for apps like for Swift,
and Objective-C code, like Instruments.
And we have Web Inspector for web developers,
creating web pages and sites.
But don't think that just because you choose
to use web technologies in your app
that you don't have any tools.
In fact, I'm going to show you today that all the same tools
that are developed to help people build websites work just
as well for web technologies in apps.
So I want to start today by showing you how
to connect Web Inspector to your WebViews in JSContext,
so you have the right tool ready to go when you need it.
Then you're going to learn about some new features
that will help you understand where time is being spent
find performance issues lightning fast.
And if you've ever wished you could see how memory is used
by your web content, we've added new timelines to Web Inspector
that I can't wait to show you.
They're going to save you
so much time finding memory growth issues.
So let's get started.
And the first thing we're going to need
to do is connect Web Inspector to our app content.
Now there are all sorts of reasons
to use web technologies in your apps.
so it's easy to swap out the logic
or add new modules without re-compiling.
And some of you may be using JSContext
with TVML in a tvOS app.
And another reason to use web technologies is
when you want to show web content.
Like a web page from a third-party website.
A site you don't control.
And for that, you're probably using Safari View Controller
and if you're not, you may want to take a look
at last year's session Introducing Safari
But if you're showing content that you own or control
or can customize content that happens to be written in HTML,
WKWebView is the best choice.
And it's essentially a rectangle
where web content is drawn into your app.
It was introduced in iOS 8 and OS 10 Yosemite.
And if you're still using a WebView or UIWebView,
you're really going to want to take a look
at upgrading to WKWebView.
In fact, upgrading to WKWebView allows you to take advantage
and the four-tier JIT compiler for a nice speed boost.
So if you want to learn more about WKWebView,
I recommend taking a look at the 2014 talk
that introduced the modern WebKit API.
So last year we added some great new features to WKWebView,
like load file URL, custom user agent strings
and the WK website data store API.
And today, with iOS 10 and macOS Sierra,
we've improved 3D-touch support.
And now we allow your app to implement those sweet Peek
and Pop events in WKWebView.
Now like I said earlier, just because you've chosen
to use web technologies in your app,
doesn't mean you don't have any tools.
In all of these cases, you can connect Web Inspector to dig
But before you can use Web Inspector,
you'll need to enable the develop menu.
So just load up Safari Preferences and go
over to the Advanced tab and at the bottom,
you'll see this check box.
And it says Show Develop menu in menu bar.
Just give that a click to check the box
and the Develop Menu will appear in the menu bar for Safari.
Now to allow Web Inspector to connect your iOS devices,
there's a setting you need to turn on.
So in the Settings app, on iOS, tap on Safari.
Then down at the bottom, tap on Advanced
and toggle the Web Inspector setting On.
Now you can connect your device to your Mac and check
out the Develop menu in Safari.
Now something that's really cool in the Develop menu,
something you may not have ever noticed before,
is you can see a list of devices attached.
There's an iPhone connected, a MacBook Pro
and the simulators there.
And all you have to do to attach to one of these
and start using the tools
for debugging is just choose the Device menu.
And you get a list of all the WebViews and JSContexts
that are running on the device.
And this Mac app here doesn't even use WebKit.
And I can connect right to it and use the tools.
Now for iOS, apps will only show up when you build
and run them from Xcode.
But when we're talking about a Mac app,
there's just one more thing you got to do.
To protect the integrity of your app,
we don't let just anyone download your app
and use Web Inspector to poke around your app.
So you'll need to add this entitlement
to your app's Entitlements File for local development.
You probably already have an Entitlements file
but if you don't, it's pretty easy to create one.
You just create a new plist with a .entitlements extension,
set the code signing entitlements path to that file
in Xcode's build settings.
So you add this while you're developing
and then you take it back out when you ship your app.
Then once you have this entitlement, your device
and app will show up in the Develop menu
and you can attach to it.
And it's easy -- just that easy to connect Web Inspector
to your JSContext and WebViews.
Okay. So we're up and running with Web Inspector and our apps.
Time to move onto some new features in Web Inspector.
and this year we have a much better one that's going
to be way more effective at helping you quickly figure
And the reason is simple.
The new profiler uses a sampling technique
that doesn't affect your performance anywhere near
as much as before.
So we had a profiler in an era before there was really
It was an interpreter.
But now we have this very powerful four-tier JIT compiler,
and the right profiler for that is really a sampling profiler.
And the sampling profiler tells you
where time is being spent in your code.
It helps you answer questions
like what code is costing us the most time?
It samples the running program every millisecond,
and it pauses execution briefly to take a quick snapshot
of all the code that's running.
And it can also take samples while running your code
with all four tiers of a JIT enabled.
So that means it's sampling
at near the true speed of your code.
And since handling breakpoints can cause code de-optimization,
we temporarily ignore them.
So while you're profiling,
you get the truest performance for your web app.
There isn't nearly the same performance cost
to using the sampling profiler.
And that literally means while profiling your code,
it can run at up to 30 times faster.
It makes the whole process of profiling your code much quicker
and easier, and you get much more accurate data as a result.
This was such an exciting development that our team,
our Web Inspector team, was able to take advantage of this
to find places where we could improve the speed
in Web Inspector itself.
So we have a sampling profiler.
Let's see how Web Inspector uses it to help us find problems.
So there's a lot going on here,
but it's actually pretty easy to break down.
And it's even more helpful
with code you're already familiar with.
what we really mean is we're recording a timeline
And this is the Events view, and it shows a list
In particular, this is for some code that uses the D3 library,
so it's even helpful for debugging code
and profiling code that's in a library that you're using.
And each entry here is an event where code is executed.
It's where a code enters
And that includes event listener callbacks,
like these animation frame entries
for request animation frame handlers.
Or script evaluated entries
And we also have some timing information here,
showing you the time cost of code that's being run.
So if anything is more than 10 or 15 milliseconds,
you're getting really close to dropping below
that smooth 60-frames-per-second performance.
Now the Events view is helpful but there's another view
that we've added for you and that's the Call Trees view.
If you've used other profiling tools,
this ought to be pretty familiar to you.
Just click this Menu and switch to the Call Trees view.
And now it shows you the accumulative time
for functions in the call stack.
And this is what we call the Top Down view, and you can use it
to dig down through the Call Tree
to uncover hot functions spending lots of time.
But my favorite view is the Bottom Up view.
It takes me right to the hottest functions, the functions
that are sampled most often.
And this is the list of the called functions,
and it's sorted by the ones costing the most time.
So it inverts the Call Tree
so you can quickly compare the function costs directly.
And you can see exactly where most
of your time is being spent.
You can expand the entries and go back up the path that leads
to the functions chewing up all the time.
And this tells you when
and where your most expensive code is getting called.
And to see this in action, I'd like to invite my colleague,
Brian Burg, to the stage for a demo.
So the sampling profiler is great
because it can take really complex content
and still profile it and get you really accurate information.
And you can make it even faster.
So to show this off, I've got this iPad app I made.
It's called Satellite Tracker.
Let me get the display here.
So Satellite Tracker will show you, right now or any time,
where the satellites are overhead.
So you can choose different places on Earth.
You can choose different satellites.
So that's great.
If you're worried about satellites overhead and want
to put on a tinfoil hat when overhead, this is a great app.
But there's a small problem is that if we have a lot
of satellites overhead, or pieces of a satellite
that blew up, in this case, the frame rate's kind of choppy.
This is definitely not 60 frames per second.
It's jittering all over.
So we can use the sampling profiler to figure
out what's going on and why it's so slow.
So what we've got to do is go over to Safari
and we go to that Develop menu.
Find the iPad here and attach to it.
And the first thing I want to do is go
over to the Frames view here and just see where we are right now,
in terms of frame rate.
So let's just start recording.
I'll switch back to my iPad.
And start doing stuff.
It'll sort of spin around here.
Maybe a change the satellites.
Change the time.
Okay. Let's go back and see.
Okay. Let's zoom way out.
Yeah. Our performance is all over the place here.
So sometimes we're getting 60 frames per second here
on the left.
In the middle, it's just sort of going all over the place
as we're changing the views.
And here we're sort of going too slow.
So if we want to figure out what's going on here,
we want to switch over to the Events view,
to focus in on that sampling profiler data.
So as Jon showed before, the Events view here is going
to show everything that went into the run loop.
And in this case, it's an animation or simulation,
so we're just rendering frames over and over.
So it's not really helpful, if we want to figure
out what's taking the most time.
So let's switch over to the Call Trees view, and here we see
that Top Down Call Tree.
And this shows aggregated over all those rendering frames
where we spent the most time.
And here we can expand this to see that D3 has a Timer function
and that calls some of our code, which draws a scene.
And you know we draw some things in the scene like satellites
and the time and these things.
So this is great, if we want
to understand what the code is doing.
But if we want to figure
out which functions specifically are really hot,
it's better to go over to the Bottom Up view.
So here we've listed all the Functions regardless
of who called them.
And we can see that fillText
and our tangent [phonetic] are our two hottest functions,
so why are we calling our tangent?
We can expand out this row here
and see who's calling our tangent.
So right here, we're plotting some satellites.
It looks like we're computing the transform
so we can draw this globe.
Okay. These things seem pretty normal.
Maybe I can pull out my math book
and make it a little faster.
Let's go up to fillText.
So and to refresh you, if we go back to the app here,
we're drawing text on the current time up there
and also for every data point.
So that makes sense.
But if we look really closely here,
we're actually drawing the time twice,
and that seems kind of strange.
So if we expand this out, we can see who's calling this,
and it seems that we're seem
to be drawing two different foreground scenes at once.
This is probably not what we wanted.
So let's figure out what's going on.
If it was the case that we were drawing two foreground scenes,
then we're doing twice as much work as we need to.
So over here, just to refresh your memory,
we have this sort of flat map.
And then we have the globe, which rotates.
let's go back into the code,
and figure out what controls switch in-between these
Maybe we messed up somewhere.
So we switch between the two globes
when we change the location.
So here's updateLocation.
Okay. So when we have one map up, we don't see the other one.
So that makes sense because here we're adding the hidden class,
and that's just going to make it not display.
And here for the globeMap, we set running equal to true
when the whole thing is running.
And also in the place we're showing it's not a global
projection, so that makes sense.
When we're showing the flat map, we don't show the globe
when it's not running.
Up here for the flat map, it seems like it's always running
if the UI is running at all.
So that's kind of strange.
Well let's go back to the map and try something.
So we'll go up to our [inaudible] data set here.
And if we go to Earth, it seems to have a better frame rate
than if we just did the globe map.
And well, that makes sense.
I think we're drawing two maps when the globe map is active
but only one map when we're doing the flat maps.
So if we go in here and change this condition --
We want it to be the opposite.
Okay. So let's stop, and see if this is the fix.
So go back to our iPad here.
Okay. This looks pretty smooth.
Let's go here.
Oh that's great.
Yeah, it looks really nice.
So let's go and check that rendering timeline again and see
if it's 60-frames per second.
So I'll switch back to Frames again
and start recording and yeah.
That's pretty nice.
I'm spinning the globe, and it slows down a little bit.
But the steady state seems to be --
okay, we're definitely under 60-frames per second.
And over here we have the bar, and if we're
under it, then we're in luck.
Okay. So now Satellite Tracker's a lot faster, so we know exactly
when to put on our hat.
This is great.
So this is a small example
of how we can use a sampling profiler to dig
into really busy content and make it even faster.
Jon's going to tell us about memory and allocations.
Thank you Brian.
So you can see that profiling is fast.
It allows you to see the true speed of your code
so you can get really accurate data.
and use it to find slow callback handlers, slow timers
or slow script initialization in the Events view.
And use the new Call Trees view to see time cost, as they pile
up across the time slice you select.
Remember that Bottom Up is your new best friend.
It really helps you find the best places to start optimizing.
Okay. So we've looked at the new sampling profiler
I'm really excited that we have better tools
for optimizing CPU time.
And we can give our users a fast experience and save a bit more
of their battery life.
And this is great and now we're going to move
on to the other side of the performance coin,
figuring out where the memory is going.
So you want to be efficient with memory in your web content
because it's a limited resource.
Being memory efficient helps your web content be able
to scale really well to handle large data sets.
Plus, using lots of memory degrades performance
and we don't want to do that.
and it can also bring down your web content
and we really don't want to do that.
Now the good news is if you're going WKWebView,
it runs in a separate process,
so it won't bring down your entire app.
But still, it's not a great user experience.
So to help you with all of this, we've added two new timelines
to Web Inspector and Safari 10.
When you fire up the new Web Inspector,
the new timelines will be off by default,
so you need to turn them on.
You just click the Edit button and just above the Timelines,
you can now configure the timelines you want to see.
So you can just work with the ones you want to work with.
Just like in the Instruments app.
So just toggle the new timelines On, and you're ready
to record a new timelines.
But you probably don't want
to keep them all running at the same time.
and Events Timeline has less overhead but there's still some.
process during garbage collection
that can have a performance impact.
Okay. So we're going to leave the Memory Timeline turned On.
And when you record a timeline, you get something like this.
You get this new Memory Timeline graph
and it shows you how memory has been allocated
across different categories over time.
And there are a series of charts and graphs
to help you understand how memory is being used
and how it's being divided up.
The Breakdown chart here shows you how memory is allocated
up your page, and for the rest
of the engine-related page memory.
And the Max Comparison chart helps you investigate
So we have a high watermark here
and that helps you see memory problems in the past.
And you can even isolate spikes, by selecting a specific slice
or a specific range of time around a spike in the timeline.
And then, you can use the category breakdown below
to see what's contributing most of the spike.
And each of these graphs here are independently scaled.
So you can easily see changes over time.
where lots of new objects are being created and referenced.
And that includes Objects like string Objects and functions
and all the engine data that supports them
like structure data and compiled code.
you're seeing garbage collection reclaiming memory.
And images shows you the memory allocated for images
that have been decoded for display,
so that's the larger image data, usually used for images
that are visible in the viewport.
And layers is showing you graphics layer memory,
memory used for WebKit's tile grid, compositing layers
and other engine layers.
Pages is everything else,
all the other things the engine's keeping track of,
like the DOM and page styles, fonts rendering data,
memory caches and system allocations.
So this breakdown gives you a great way to ensure
that memory use lines up with your expectations.
you'd expect the largest category --
And you'll likely see more changes over time
in the Timeline graph.
But for an image-heavy page like a gallery, for example,
then the layers and images categories would likely be the
largest, with more changes over time.
So that's the Memory Timeline, new in Safari 10.
It's one thing to see memory growing over time,
but it's another thing pinning it
And a very powerful tool
What's powerful about Snapshots is that you get a snapshot
And you can dig in to see everything that's allocated.
But it's even more powerful when you have two Snapshots,
and this allows you to go back later and compare the two.
And comparing Snapshots is one of the most powerful tools
for answering the question, am I doing unnecessary allocations?
So to really make use of this, you need multiple Snapshots.
So that's why, by default, we take one every 10 seconds
and also at the beginning and end of a recording.
So the Snapshots are plotted on the Timeline,
so you can correlate them
to things happening on other timelines.
I just have the others turned off here for now.
And the Snapshots are listed below with a few details,
like timing and size of the heap.
Now, to dig into a particular problem,
you'll often need a Snapshot, both before and after
where you think a memory issue is happening.
And there are three techniques.
You can rely on the automatic ones, every 10 seconds.
Or you can take one yourself
by pushing the Take Snapshot button.
Or you can do it from your code.
And really the easiest way to zoom into an issue is
to modify your code a little.
You call the takeHeapSnapshot API
and pass it a custom label argument of really anything
that can help you identify it later.
And again, you want a pair of Snapshots, both before
and after the code you think is causing the problem.
Now you could also use this by taking a Snapshot
between doing some work in a loop.
So just some quick things to keep in mind
about the takeHeapSnapshot API.
Remember that Snapshots do add some extra process during
garbage collection and that can impact performance,
which you'll definitely notice
if your code is firing off a lot of Snapshots rapidly.
You'll also want to capture the differences before
and after code that's doing some work or at some point
between work in a loop.
And don't leave this in.
I mean, if you leave this in,
for most customers it'll be okay.
But if anybody's running Web Inspector, they're going
to be taking all these Snapshots,
and you probably don't want that.
So just remember to be sure and take it out before you ship.
So what are these Snapshots really show you?
Let's take a look.
You just click on the Snapshot icon on the Timeline
or on the Arrow button of any of the Snapshot list entries.
And you'll get this list of Objects
that were allocated in the heap.
And we have two views for Snapshots.
This is the Instances view, and it shows you a list of Objects
in the heap, grouped by their class.
And the other is the Object Graph view.
And this is really an overview of everything
and everything that's owned by everything.
So if you're readily familiar with the code,
this can be a useful way to confirm things
or where they should be.
But actually the far more useful view is back in the other one,
in the Instances view.
And it's powerful because you can easily find Objects no
matter how deep-down the property path they are.
And the Count here can help you realize potential issues
when they don't meet your expectations.
Like was I really expecting over 4000 string Objects?
So you can expand the Classes and see all
of the allocated Objects of that class.
Then to figure out what something is there's all these
The Class is a clue.
Another is the actual properties of the Object.
It's a really quick way to know what it is.
But the easiest way to know what an Object is is to hover
over this Object Identifier here and you get this.
Look at this.
It literally shows you the shortest path to the Object.
This is telling you exactly what is keeping the Object alive.
It almost always gives you answer you need,
This is the kind of thing that cuts right
through the confusion.
But the most important feature and really the entire point
of this is to be able to compare two Snapshots.
Now watch this.
Once you've collected some Snapshots,
you just click the Compare Snapshots button here,
and you select a Baseline Snapshot
and a second to compare against.
And boom. You get a new Comparison Snapshot to dig into.
Now this is a really big deal
because now we're only seeing the Objects that are new
between two points, between our two Snapshots.
Expanding the Object Class group we can see all
of these Object Allocations.
And their previews are showing their names and what looks
like some telemetry data.
And that's a clue that these are Satellite Objects.
And the pop over here shows that they're
in the Satellites Array property.
Since this is a Snapshot comparison,
these Satellite Objects are all newly allocated.
And that's a big clue as to what the code's doing.
So to show you all of these new memory features in action,
I'd like to invite Brian back up to the stage for another demo.
So I gave Satellite Tracker to my buddy, Ed, and he stayed
up all night playing with it.
And he had a lot of fun,
and he never got tracked by the satellites.
But there's a problem.
Eventually it just kept getting slower
and slower the longer it was up.
And to me that sounded like you know classic memory.
The longer the thing is open, the slower it goes.
So I want to look into Satellite Tracker
with these new memory tools to figure
out if we're leaking some memory somehow.
So the first thing I'm going to do.
Okay I've got iPad here.
I'm going to go back to the Web Inspector --
And inspect the app.
And the first thing I like to do,
when I don't really know what the bug is, here is I want
to use the Memory Timeline.
And that's going to show me sort of like an overview
of what's going on on the page.
So let's start recording.
I'll switch back, and I'm just going to switch back and forth
between two satellites.
Maybe I'll add some effects here.
Okay, I'm switching back and forth.
Okay. Let's see what's in the Timeline.
So in the Timeline overview you can see a stacked line graph
showing all the different parts and the relative size,
but if you click here, you'll get that more detailed view.
And so there's no images on this page.
This is all canvas.
The layers is pretty flat.
The page sort of goes up and down,
stuff's getting garbage collected.
you see that like you know some stuff gets garbage collected
but overall, it's really just going up over time.
And if we had this running all night, yeah,
it would probably go up a lot more.
So the next step here is to start taking heap Snapshots
or allocation Snapshots so we can figure
out what's being allocated over time.
So to do that, we're going to start a new recording.
And one cool quick trick here is you can do Shift Click
or Shift Space, and they'll start a new recording
and not append to the old one.
Oh wait. I forgot to change our Timelines here.
So let's put away Memory.
And let's put in Allocations instead.
Okay. So let's start recording.
Go back to the iPad.
Here I added this little takeHeapSnapshot button,
so I already added some calls to the console
about takeHeapSnapshot when we switch
between the two satellite groups and some other actions.
So for this recording, I'm going to rotate and then I'm going
to switch between two satellites over and over.
We should look at Spy Satellites.
That seems kind of relevant.
Okay. And you'll notice it starts to stutter a bit
because we're taking Snapshots of everything
So this you know we're making lots of Objects,
so this is going to slow down your app a lot.
So it's important to not take lots of Snapshots.
You want to take them only on important times.
So here, you can see these Ss in the box
and those are the Snapshots we took.
So if we zoom in over here,
we can see that there's pretty steady memory growth over time
as we start switching between these satellite sets.
So if we want to investigate this, like Jon said,
we need to start comparing two of these
to see what's being retained at the end.
So let's go between Snapshot 9 and 11.
And right away, we can see a bunch of stuff
that was allocated between Snapshot 9 and 11
and is still alive right now.
So that's a pretty good sign that it's being retained
and you know we probably didn't mean to do that.
So we can start looking at what these things are.
There're some Arrays.
It looks like we have Arrays full of coordinates.
And you know we use coordinates in lots of different places
in this app, but if we hover here,
we can see the path to these things.
So these seem to be saved in trajectoryHistory,
which is what we use to make those trails behind
So okay. That's fine,
but I don't think we should still have this
trajectoryHistory for satellites who are no longer showing.
That seems kind of like a bug.
We allocate some Objects too.
And this is sort of strange because you know
between Snapshot 9 and 11,
we've already seen these satellites before.
So I wouldn't expect that we're making new Objects
for each satellite.
We should just use them
if we already fetched the resources for them.
So and then here's a bunch of coordinates and telemetry stuff,
so it seems like we might be
like re-parsing them or something.
But I'm not sure, so one thing I'd like to do
in this view is you know we have lots of Objects.
You can't read all of them,
but what I like to do is find something that's fairly unique.
And in this Snapshot here, we have lots of Strings,
lots of Arrays, lots of Objects.
But there's only one Promise that's retained
between these two Snapshots.
So I think if I want to debug this, I should start looking
in our code to see where we're using Promises because it seems
that that's being leaked with some other stuff.
So we search for Promise.
Okay. There's D3 library, and here's our code that uses it.
Okay. Let's go to this one.
Okay. So in the [inaudible] I did,
we switched between the satellites a lot
and then the code that's called loadDataset.
So someone left comments.
So here it looks like someone requests we change
So here we asynchronously load that data
for the satellites from the TRL.
When it comes back, we're going to parse it,
and then we're going to parse it some more
with the satellite plotting library.
And then we're going to save it to our list of satellites.
That's all well and fine, but back in Inspector,
it seems that we're leaking this Promise every time.
So if you look more carefully, what if we switched
to a data set that we already loaded before?
It seems like we're not even checking for that case.
So if you look more carefully up here, every time we switch
between the two satellites,
we're doing a new network request.
So if we go to this Timeline,
it seems like we're requesting the same debris field data
over and over.
And that makes sense because if we go back
to the code, we request that.
Then we make a bunch of Objects when we parse it.
And then we push it onto an array of satellites,
and that thing never really gets cleared as far as I can tell.
So it seems like we're just doing a lot
of unnecessary work and then leaking it.
So I think what we can do here is to check
if we already have this parsed satellites Object.
Because this is a Promise, if this thing already exists,
we can still call .then on it.
Since it's already solved,
next time we evaluate the Promise reactions,
it's going to go through and set the satellites onto the map.
So let's add a [inaudible] here.
And if this doesn't exist, then we'll make it --
Okay. Let's stop and rerun and see if this fixes this.
So we need to go back and reattach --
And okay, here's our app, and we're going to start recording.
And when we go back to the app,
I'm going to turn on the Snapshots.
Turn on some effects.
And we'll go down south.
Okay. So Spy Satellites.
Science Experiments by Satellites.
So if we go back here, we're seeing a lot less memory growth.
Maybe 1 megabyte, instead of like 4 or 5,
so there might be some more leaks in here.
But at the end of the Snapshot, we have about as much memory
as when we started rendering this thing in the beginning.
So I think we've fixed that particular leak.
So this shows how we can use the Allocations and Memory Timelines
to figure out where we're leaking memory
in apps like this.
And it's great because like this app has a lot of stuff going on.
But still, with the dipping [phonetic] functionality,
we can really drill down to what's changing
in some Timeline we care about.
Okay. So that's the end of Satellite Tracker.
Back to you, Jon.
Thank you Brian.
You can see how it really amazingly quick
and simple these new Timelines make it
to zero in on memory issues.
So remember you want to use the Memory Timeline to figure
out how memory is being used and what's driving memory spikes,
so you have an idea of where to go look.
And then, take multiple Heap Snapshots
into code that's driving the memory growth.
Also, don't forget to get rid of takeHeapSnapshot
from your code before shipping.
And remember the performance impact
So that's a look at the new Timeline instruments available
in Web Inspector with Safari 10.
I think you're really going to love using them.
And as I wrap this up, I want to leave you
with some next steps to take.
I want to encourage you to reconsider WKWebView,
if you've not made the switch yet.
And turn on the Develop Menu in Safari Preferences,
connect Web Inspector to your app and start taking advantage
of these new features.
Save a ton of time using them with the Bottom Up view
and Call Trees for the best places to start optimizing.
The Memory Timeline to quickly see how memory spikes
And Heap Snapshots to easily explore
and compare Object allocations.
And stay updated on features.
There's more this year in Web Inspector and WebKit
that you can take advantage of in your app's web content
to deliver great in-app experiences.
Along with the features Brian and I showed you today,
our team added some other features
to Web Inspector over this past year.
Quick Open will jump you right
into the resources loaded with your page.
And Tail Call Stacks will now show you Tail Call optimized
functions in the debugger.
And earlier this spring, we shipped Safari 9.1 on OS 10,
and with an updated Web Inspector with it.
And that shows the Pseudo Elements
in the DOM Tree of the Elements tab.
And there's also the new visual style sidebar.
And if you aren't already aware,
Web Inspector is a developer tool that's created as part
of the WebKit Open Source Project.
WebKit is the web browser engine that's used to power your apps
and drive WebViews and JSContext.
And of course, it's also the engine behind Safari.
In this past year, our teams added great new features
We hit 100% support for ES6.
We improved support for the recommended IndexedDB Standard.
We also added Shadow DOM support and WebDriver, CSS Variables
and the Picture Element.
So there's a lot going on and as an open source project,
you can follow development as it occurs.
Most of you here will want to take advantage of this.
And if there are some of you here that want to enhance some
of these, you have that opportunity
because of the way WebKit is an open source project.
So you can find out more
about the WebKit Project on WebKit.org.
And the WebKit team, the engineers behind WebKit,
blog about development work.
In fact, we have two blog posts up now
that cover the Memory Timelines and the Sampling Profiler
that we talked about today.
So if you want to get more information,
you can dig in there.
We also have a feature status page
that gives you at-a-glance updates
for our web standards progress.
And there's also links to downloads
for WebKit nightly builds and our latest browser,
the Safari Technology Preview.
It's updated every couple of weeks,
with an updated WebKit engine, so you can try out new
and experimental features
in WebKit every two weeks as it's improved.
Our teams pore a lot of work
into Safari WebKit and Web Inspector.
And the Web Inspector team was able
to use these new performance features we showed you today
to find issues and deliver faster performance
in Web Inspector itself.
I can't wait to see what you'll do with them.
So for more information, you can watch this session
and download the slides at developer.apple.com.
There are lots of other relevant sessions earlier this week
as well as in past years.
You can find those on developer.apple.com as well.
So on behalf of Brian, myself, Safari and the WebKit teams,
thank you for being here.
enjoy the rest of WWDC.
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.