Transcript
[ Music ]
[ Applause ]
>> Good morning, everyone.
Welcome to Session 203
Introducing Desktop-class
Browsing on iPad.
My name is Charles Ying, and I'm
joined today by Wenson Hsieh and
Beth Dakin from the Safari
WebKit teams.
We are so thrilled to tell you
about desktop-class browsing, a
major new advancement to the web
platform on iPad.
As you heard in the keynote,
Safari on iPadOS is now a
desktop-class browser.
Now, what does that mean?
It means getting more done with
Safari.
You can now use Safari for all
the great things you want to do
on the web.
Safari is the new download
manager, so you can download and
upload files while using other
tabs and even other apps.
Safari's new controls for
full-page zoom, hiding the
toolbar when you need more space
to work and per-site preferences
to tailor Safari to your
workflow.
And Safari now has all the
keyboard shortcuts you expect
from a desktop browser to get
things done fast and stay
focused on what you're doing.
But at the heart of it all is
browsing desktop websites.
We've been working on web
browsers for a long time.
When the original iPhone was
first introduced, all websites
were big and made for mouse
input.
iPhone made these websites work
out of the box without requiring
them to change to work with a
small screen and Multi-Touch.
iPhone would scale websites to
fit and boost text sizes so you
could read them.
Then you used new web APIs to
make your websites take full
advantage of iPhone's
capabilities.
And the results were fantastic.
Some of you used responsive
design techniques to make your
websites flexible on screens of
any size.
Those websites look great on
iPad today.
But it was also common to have
two different versions of the
site, one for big screens and
one for small screens.
iPad used the mobile user agent
which meant it got small screen
websites.
So new in iPadOS, iPad will now
present itself to websites as a
Mac.
This is not just user agent
change, but a set of deep
fundamental changes to WebKit
that add up to a great new
experience.
Let me walk you through two of
these changes.
Some desktop websites are
designed to show lots of
information on a large screen.
These websites were zoomed in on
big iPads, and you couldn't see
as much information as the
desktop browser.
WebKit used a viewport to lay
out websites that was the same
on every iPad size, the iPad
mini all the way up to the
largest iPad Pro.
So new in iPadOS, viewports will
now match iPad screen size.
Now websites can use all the
space available on bigger iPads.
With smaller iPads, we'll scale
websites to fit everything on
screen and boost text sizes for
comfortable reading just like
iPhone.
You can now see more of the
website at once just like a
desktop computer, make it easier
to get things done.
Some desktop websites are
designed for mouse input.
New in iOS 13, iPad optimizes
how touch input adapts to mouse
input.
So websites like this one using
mouse hover now work out of the
box.
And for web apps that want full
control, you can now use Pointer
Events.
This is just a taste of the
dozens of web APIs and features
new in iPadOS that you can take
advantage of in your web apps.
You'll hear more about them from
Beth later on.
All of this adds up to a
powerful browsing experience
that you expect from your iPad,
and that's desktop-class
browsing, new in iPadOS.
Now --
[ Applause ]
Now, we want to show you how
your apps and websites can take
full advantage of these new
capabilities.
For app developers, we'll show
you how to use desktop-class
browsing in your app.
And for web developers, we'll
show you how to make your
websites work even better on
iPad.
We'll start with apps.
There are four common ways apps
use web views; following links
to web content and browsing
within your app; web browsers,
where browsing is the primary
focus of your app; hybrid apps,
where web technologies are a
part of your app's user
interface and authenticating
users with a third-party
services using OAuth.
First up, link following.
The best way to follow links and
stay within your app is to use
Safari View Controller.
Just like Safari, Safari View
Controller has great features,
including Autofill and Reader.
So I'm happy to say that if
you're using Safari View
Controller, you get
desktop-class browsing for free.
There's nothing for you to do.
Safari and Safari View
Controller automatically choose
a browsing mode to give you the
best experience for your
situation.
Let me explain.
iPad is the perfect device for
browsing the internet.
It's like holding the web in
your hands.
And depending on what you're
doing, iPad can be used in many
different ways.
For example, some desktop
websites are pretty small on
iPad mini and are more
comfortable to read with a
mobile browsing experience.
When you look at a desktop
website in narrow Split View or
Slide Over, the same thing is
true.
In these cases for narrow
windows, iPad mini mobile
browsing can be better.
Safari and Safari View
Controller will automatically
choose a browsing mode to give
you the best experience.
Safari View Controller takes
care of everything you need to
follow links within your app.
What if your app is a web
browser?
Here's what you do.
First, build your app with iOS
13 SDK to turn on desktop-class
browsing.
Then look at how you're setting
the user agent.
If using the custom user agent
property, we recommend instead
to use WKWebView configurations
applicationNameForUserAgent
property.
When you provide an application
name, WebKit does the right
thing to fill in the rest of the
user agent for you.
WKWebView also is the new
WKWebpagePreferences API for
setting a preferred content
browsing mode.
There are three modes;
recommended, which does what
Safari does; mobile; and desktop
modes.
In most cases, Safari's
recommended mode is what you
want.
Your web browser might also want
to let users switch between
mobile and desktop modes or have
per-site preferences.
For these cases, WebKit has a
new WKNavigationDelegate API to
specify a content mode
preference at navigation time.
You'll see a demo from Wenson of
these new APIs in a bit.
In addition to web browsers, web
technologies can be used to
build parts of your app's
content or user interface.
If your app uses WKWebView this
way, build your app with iOS 13
SDK to turn on desktop-class
browsing.
Then test your app's use of
WKWebView.
In most cases, you're done.
But in the rare case that you
need to opt out of viewport
sizing, you can set the
preferred content mode to
mobile.
Overall, it's pretty easy.
WKWebView does most of the work
for you.
Oops. Finally, web views are
used to authenticate users with
a third-party service using
OAuth.
If you're doing this,
ASWebAuthenticationSession is
the best way to authenticate.
New in iPadOS,
ASWebAuthenticationSession now
presents a form sheet on iPad to
show the authentication
interface while staying within
the context of your app.
Given this new presentation,
ASWebAuthenticationSession loads
websites using a mobile content
mode.
Just like Safari View
Controller, there's nothing for
you to do if you're using this
API.
To recap, Safari View Controller
and ASWebAuthenticationSession
do the heavy lifting for you.
And if using WKWebView, you have
all the tools you need.
And now I'd like to invite
Wenson to the stage to show you
a demo of how this works.
That's it.
[ Applause ]
>> Thank you, Charles.
So when I'm not busy working on
a web browser, I like to take my
mind off of things every now and
then by writing more web
browsers.
So today, I'd like to introduce
Shiny Browser.
It's a WKWebView-based browser
that I've written, and here it
is on Google Docs.
So sometimes I need to
collaborate with my friends
using Google Docs.
For that, I'd like to be able to
use my browser, but as you can
see here, it just tells me to
download the app.
I'll bet that if I had requested
a desktop version of this
website, it would give me full
access.
So as I just learned from
Charles a moment ago, WKWebView
in iOS 13 should request the
desktop version by default on my
iPad Pro.
So I'm going to recompile my
browser against the iOS 13 SDK,
and that should give me the
desktop website.
So I'm going to switch over to
Xcode here, and I'm just going
to hit Command-R to run.
That's going to compile my app,
and we should see the desktop
website.
Okay, well.
Unfortunately, we're still
getting the mobile version.
Let's see if we can try to debug
this together.
Now, let's take a step back.
Google Docs thinks I'm a mobile
browser.
It's probably using the user
agent string to make that
determination.
What I'm going to do is connect
to the web view using Web
Inspector and print out the user
agent string.
Maybe that'll give me a hint as
to what's going on.
So now I'm going to launch
Safari, go to Develop menu and
target Shiny Browser here.
So I'm going to switch over to
Console tab, and I'm just going
to type navigator.userAgent and
hit Enter.
So let's zoom in on this user
agent.
There's a couple of interesting
things I want to point out here.
First of all, the word iPad
appears in the user agent
string, so clearly it's not
desktop-class.
But what's more, there's this
Version/1.0 ShinyBrowser/1.0
here.
It's kind of mysterious.
Where is this coming from?
Well, I like to know myself, so
I'm going to copy and paste into
the search field in Xcode.
So I'm going to Command-F to
find and just paste.
Okay, well as it turns out, I
had code to override the entire
user agent string using the
custom user agent property of
WKWebView.
I must have copied and pasted
this from the internet at some
point and forgot about it since.
Maybe it was a reasonable
solution at the time, but as
I've just learned from Charles,
there's an alternative.
What I can use instead is the
application name for user agent
property on WKWebView
configuration.
So let's change this to use that
instead.
First thing I'm going to do is
remove this custom user agent
code and go up here where I'm
creating my web view.
So what I'm going to do here is
first create a WKWebView
configuration and then set the
application name for user agent
to what I want, Version/1.0
ShinyBrowser/1.0.
And finally, I'm going to create
my web view using this
configuration.
So that was a very small tweak.
I'm going to recompile my app
and see what I get this time.
All right, so as you can see,
we're now getting the full
version of Google Docs, and I
can finally collaborate with my
friends.
That was pretty easy.
But now let's take it one step
further.
One of the features that my
users have been requesting for a
while is the ability to switch
between the desktop and mobile
versions of a web page.
With the new API in iOS 13, it's
now fairly easy to implement, so
let me show you how.
First of all, notice I have this
button in the top right-hand
corner that contains some extra
options like Add To Favorites
and Share.
What I'm going to do is I'm
going to add a third option here
to allow the user to change
content modes.
So that's going to either say
Request Mobile Website or
Request Desktop Website.
Okay. So let's jump into Xcode
and implement that.
I'm going to go down here, add
this helper function that
presents a list of
UIAlertActions.
So far, as you can see, I've
have Add to Favorites and Share.
What I want to do is add a third
action here.
But before I do that, I'm going
to make a new helper function.
It's going to help me create the
UIAlertAction.
And of course, I'm just going to
use it up here.
Okay. So that should get my
action.
And now I want to draw your
attention over to this
currentContentMode instance
variable.
So what am I doing here?
Well, I'm checking if the
current content mode is equal to
desktop.
And if it's equal to desktop, I
want the string to say "request
mobile website" and vice versa.
Okay, so that should make sense,
but the question is "How do we
keep track of
currentContentMode?"
How we know what it is?
Well, I'm going to scroll down
here where I've implemented my
WKNavigation delegate, namely
the didCommit navigation method,
and what I'm going to do here is
use a new API in iOS 13.
So in iOS 13, the WKNavigation
has a new property called
effectiveContentMode.
This is only ever going to be
mobile or desktop and is perfect
for my use case.
What I can do is set
currentContentMode equal to the
navigation's effective content
mode when I commit a navigation,
and that should help me keep
track of what content is
currently being loaded.
So now, I should be showing the
right string.
But I want to draw your
attention back to this action
handler.
We still need a way to say,
"Let's prefer to load mobile or
desktop content for given host
name."
To do this, what we're going to
do is save a dictionary of host
names to content modes, be it
mobile or desktop, whatever the
user is tapping.
So I'm going to implement the
action handler now, and what I'm
going to do here is simply get
the host name and set that in
the dictionary to either mobile
or desktop.
Then I'm going to tell the web
view to reload.
Okay, so this is where we
properly are,
contentModeToRequestForHost
dictionary.
But of course, we still have to
use it somewhere.
So I'm going to scroll down here
back where I have the navigation
delegate, and I'm going to add a
new method.
I'm going to implement, decide
policy for navigationAction.
This is very similar to the
version you might be familiar
with, except, in iOS 13, this
one now contains web page
preferences as a parameter.
So this preferences right here.
So what I can do with these
preferences is set the preferred
content mode equal to the
content mode I would get by
looking it up in my dictionary.
And just as a refresher, this
contentModeToRequestForHost
dictionary is the same one that
we populated up here in the
action handler just a moment
ago.
Okay. Finally, last but not
least, we have to remember to
actually call our
decisionHandler with these new
preferences, and I should be
good to go.
So I'm going to recompile my
app, and let's see how our
feature works.
I'm also going to switch it up
this time.
Instead of Google Docs, let's go
to an image gallery.
So this is the desktop version
of Shiny Pics.
And what I'm going to do here is
use the Extras menu to request
the mobile website.
So this is what the mobile
website looks like, the version
of Shiny Pics.
And, I mean, I can tell it's
kind of the mobile version of
the website, because it really
looks like a page that was
optimized for a phone, not so
much my iPad Pro.
I think on my iPad Pro, I
actually prefer the desktop
version of Shiny Pics.
So I'm going to just tap Request
Desktop Website, and I'm back to
the desktop version of Shiny
Pics.
Okay, so to recap, I first
ensured that my browser can load
desktop content, and then I used
new API to implement a Request
Mobile or Desktop Website
feature.
But so far, we've only shown
some new tools for you app
developers in the crowd.
Of course, we have news for web
developers as well.
And to tell you more about it,
I'd like to invite Beth to the
stage.
[ Applause ]
>> Thank you, Wenson.
That was great.
So what does all of this mean
for web developers?
If you have a responsive
website, probably very little is
different now.
But we do have some new tools
that you can use to maintain and
improve your responsive website.
I also have some best practices
to share for web development on
iPad so that whether your site
is currently responsive or a
big-screen website designed for
desktop computers, you can make
it a truly great experience on
iPad.
There are six new features I'd
like to discuss.
Some of these are purely
developer features like pointer
events, and some are more like
new end user features that have
developer implications like
accelerated scrolling.
Let's start with Pointer events.
One of the biggest problems of
loading desktop content on an
iPad is reconciling mouse and
touch input.
Sometimes websites are written
so that the small-screen version
uses only touch events, and the
big-screen version uses only
mouse events.
Our goal is to keep websites
using only mouse events working
as much as possible on iPad even
though of course there's no
mouse.
So when a user taps, WebKit will
send the mouse-down, mouse-up
and click event for
compatibility just like always.
Most hover events get dispatched
at that time too, and I'll dig
into that more later.
However, mouse move really
doesn't make sense on iPad.
The closest analogy is moving
your finger on the screen, but
that's how you scroll.
So we did try to send mouse-move
events at the same time that we
send touch move, but it actually
caused many more problems than
it solved, so we decided not to
do that.
It conflicted with scrolling.
If you need something like mouse
move, then we have a great
solution, and that's pointer
events.
We added support for pointer
events in WebKit in iOS 13 and
macOS Catalina.
This is a web standard that
provides a layer of abstraction
between user input and your
website, so the same API can be
used for input that comes from a
mouse, from touch, or from a
pencil.
Pointer Events are easy to
adopt.
If you have existing code to
support mouse events on a
desktop that looks like this,
the code to support Pointer
Events is really similar.
Just the event name is
different.
But let me expand the context to
show how I'll really use this.
I'll actually use feature
detection to determine if
pointer events are supported.
I'll keep the mouse-event
listener in order to accommodate
older clients only when pointer
events aren't available.
The PointerEvent object actually
inherits from mouse event, so I
literally don't have to change
anything about my
updateInteraction function to
get this to work unless I want
to take advantage of additional
parameters on PointerEvent that
are specific to mouse or pencil
input, which is huge.
It really is this easy to adopt
pointer events.
Pointer events can coexist with
mouse and touch events.
But be careful when
intermingling event types that
map to the same user action like
here.
There's no else clause, so both
of these events will be
registered, and my function will
be called twice every time the
pointer moves, which might mess
up some of my state.
Also, you don't need to register
all three types of events if you
want to distinguish between
mouse and touch interaction on
devices that support both,
because pointer events allow you
to know the difference.
If you want to cancel default
web browser behaviors like
scrolling, then on Mac you'll
use preventDefault just like you
would for mouse events.
On iOS, preventDefault won't
lock all browser behaviors, so
you should also use the
touch-action CSS property.
Touch-action is actually really
awesome.
It's easier than writing
JavaScript and it allows you to
make more granular decisions
than preventDefault.
Here I am using it to block all
browser behaviors with the none
value, but I could use it to,
for example, prevent scrolling
but still allow zooming.
Touch-action also enables more
efficient user interactions,
because it's declarative, so
using touch-action and pointer
events on your website may
actually be a performance
improvement.
So we have compatibility mouse
events to keep desktop sites
working, but that's a stopgap on
the iOS, and the solution is
pointer events.
They're easy to adopt.
They let you do everything you
can do today with mouse or touch
events, and they ensure quick
responsive user interactions.
This is a great solution to all
of the problems that we face
reconciling mouse and touch
input.
As I mentioned, WebKit also
sends hover events for the sake
of compatibility.
Hover is tricky on touch screen
since the hardware just doesn't
support it.
We've changed the way that mouse
hover detection works in iOS 13
to keep desktop sites that rely
on it working well.
If you tap on an element in a
web page, the response to hover
will perform that hover, and if
a meaningful change has happened
in a web page as a result of the
hover, will stop there.
The user can tap a second time
if they wanted to click.
In iOS 13, WebKit detects many
more changes as meaningful than
ever before.
This adds critical functionality
to many desktop websites that
rely on hover for essential
parts of the web page like this
web page which uses it for
menus.
We're finding that this is
working really well, but
fundamentally this is a
heuristic where the browser
engine is trying to interpret
design intent, so it won't
always be perfect.
We have some best practices to
keep in mind when you're using
hover.
First, you should always provide
another way to access meaningful
content just in case WebKit
misses something.
This is important for
accessibility too.
Wenson will dig into this later
with the demo.
You should also avoid forcing
the user to tap twice for the
most common interactions.
Remember, if WebKit detects that
a meaningful change happened on
hover, that means that the user
has to tap a second time if they
actually wanted to click.
So if you have an element on
your page where you think users
will far more often want to
perform that click than they
will want to see or interact
with the hover content, then
consider making some changes to
avoid that two-tap speed bump
that you get from using hover on
clickable elements.
Speaking of efficiency, keep
hover snappy.
If you start timers in response
to hover, WebKit will wait for
those timers to fire in case
meaningful content is added to
the web page on a delay.
In a worst-case scenario,
something starts the timer, but
then nothing meaningful happens,
so WebKit automatically performs
the click.
But in the meantime, waited
several 100 milliseconds longer
than usual, and the tap feels
slow to the user.
We believe that we've made mouse
hover detection better than ever
on iOS.
Still, we suggest that you use
it only as progressive
enhancement and make sure your
site works great even without
hover.
WebKit on iOS has always had
hardware accelerated scrolling
for the mainframe, but I'm
delighted to say that WebKit now
has hardware accelerated
scrolling everywhere, meaning
that subframes and overflow
scroll regions now have buttery
smooth fast scrolling out of the
box just like the mainframe.
[ Applause ]
That's right.
Even subframes.
On older versions of iOS, WebKit
would force subframes to be the
full size of their contents, so
they didn't actually scroll
individually and could end up
being much larger than you
defined them to be in your code.
Now on iPad, frames will be the
size that you specify, and
they'll be able to scroll just
like you expect from a desktop
browser.
This has been such a commonly
requested feature that there
were two popular ways to work
around the fact that WebKit did
not have support for this by
default.
First, we added a CSS property
-webkit-overflow-scrolling:
touch; that developers could opt
into when fast scrolling made
sense for them.
We never made this the default,
because it creates a CSS
stacking context which affects
the front to back layering of
elements on your page.
And second, some of you built or
used JavaScript libraries that
would use touch events to
emulate fast scrolling.
Neither of those techniques is
needed now.
And in fact, WebKit overflow
scrolling touch a no-op on iPad.
So you should test how hardware
accelerated scrolling everywhere
affects your content, and if you
were using a technique to work
around the absence of this
feature in the past, you may not
need it anymore.
Next, I want to tell you about
our new automatic viewport and
text sizing behaviors.
We developed our new automatic
behaviors because web pages
should fit with legible text on
iPad.
To elaborate, websites that
weren't necessarily built for
iPad should display to fit on
iPad, meaning that they
shouldn't scroll horizontally
unless they were designed to do
so, and all text should be
comfortably legible for most
people without additional
zooming.
We need automatic behaviors to
achieve these goals, because
some desktop websites are built
in a fixed width that is wider
than an iPad.
We found that a number of sites
that are very wide like this one
also declare incorrectly to be
responsive in the viewport meta
tag, which is kind of
unfortunate.
Let me step back and explain
what I mean by that.
The viewport meta tag was
originally created to address
problems that can result from
displaying content that was
built for a desktop computer on
a small screen.
So it's perfect for this, right?
Well, this particular viewport
value is meant as a promise to
the browser engine that the
website was designed
responsively, meaning that it
will adaptively reflow as the
window size changes.
In previous versions of iOS, we
would typically take this
promise at face value, and we
wouldn't apply any automatic
viewport adjustments on websites
with a meta tag of width equals
device width and initial scale
equals 1.
But that led to a bad experience
on many desktop websites on
iPad.
This site should be able to fit,
but it didn't.
So new in iPadOS, WebKit will
ignore the meta tag if it
promises to be responsive but
then actually lays out to the
width that is greater than the
device width.
We found that this results in a
much better experience overall.
If your website is designed to
scroll horizontally and is
incorrectly being scaled down,
you have an easy fix.
You just need to add one more
value to your viewport meta tag,
shrink-to-fit=no.
This is not a new value.
We added this in iOS 9 because
we ran into the same problem
with websites in Split View or
Slide Over.
So shrink-to-fit=no will now
prevent automatic shrinking for
websites in Split View, Slide
Over and for wide desktop
websites.
You may have noticed that even
though the boxes on this website
shrank down to fit in the
viewport, the text in the header
actually got bigger, and that's
because we'll automatically
adjust the text size on web
pages that have been shrunk down
in this manner in order to keep
the text legible.
If you want to control the
viewport and text size on your
website, the very best thing you
can do is to adopt responsive
design and ensure that your
content adaptively lays out to
fit any window size.
Responsive design is a dense
topic, so I won't attempt to
give a tutorial here, but there
are lots of great ones online.
So in summary, WebKit has new
viewport and text sizing
behaviors that make web pages
fit with legible text.
The best way to control this
from your end is to adopt
responsive design, and if you
have a website that is meant to
scroll horizontally, then you
can get everything right with
the viewport meta tag.
Next, I want to tell you about
the visual viewport API.
To do that, I first want to make
a distinction between the visual
viewport and the layout
viewport.
We were just talking about our
automatic viewport sizing
behaviors and the viewport meta
tag.
Those are used to define the
layout viewport along with the
screen and window size.
The window size can change when
the user enters or adjusts split
screen.
Responsively designed websites
will react to that change using
media queries or perhaps by
listening to the resize event
and JavaScript.
Max and min with media queries
will be assessed whenever the
layout viewport changes, and the
resize event will fire then as
well.
So if the user rotates the
device, the layout viewport
changes again, and your content
can react to it with one of
these techniques.
But then there's the keyboard.
The keyboard presents us an
overlay, so it doesn't change
the layout viewport, and media
queries and the resize event
don't react.
So if the user taps on the name
field here, the keyboard comes
up.
And clearly, that's affected
what's visually onscreen.
That's what defined by the
visual viewport.
The layout viewport here is the
normal window size, and the
visual viewport is the section
that is currently visible and
unobscured.
We've heard for a long time that
developers want to react to
changes in the visual viewport.
In this sample web page, for
example, the Donate button is
now obscured, and the site would
be more useable if it always
stayed onscreen.
In iOS 13, we can finally
address this problem with a W3C
standard visual viewport API.
You can use this API to monitor
the resize of the visual
viewport.
This event will fire when the
keyboard goes on or off-screen.
And it will also fire in Safari
as the smart search field
collapses while scrolling.
Now I can keep the Donate button
visible.
We think the visual viewport API
is a great tool for taking
advantage of the big iPad
screen.
Let's talk about streaming
video.
Those of you who offer streaming
premium video content in web
browsers probably already know
that HTTP Live Streaming or HLS
is the best way to do it.
HLS is available on iPhone, iPad
and Mac.
It's an easy solution to a hard
problem, because it does all of
the heavy lifting for you.
It works well with CDNs, and you
get things like AirPlay
integration for free.
However, some desktop content
uses Media Source Extensions or
MSE instead.
MSE is an API that enables video
providers to have explicit
control of the data that is
served to the user.
For example, you can manually
upgrade or downgrade video
quality in response to bandwidth
changes.
If you have existing content
that uses MSE, I have great news
for you.
MSE is available for desktop
sites on iPad for the first time
in iPadOS.
If you have an existing engine
that uses MSE for your desktop
site, it just works on iPad.
And if you use a JavaScript
library that implements an MSE
engine, that will work too.
With both HLS and MSE as
options, streaming video is more
powerful than ever in Safari on
iPad.
These new features should help
you make your web content sing
on iPad.
I talked about some best
practices along the way, but I
have a few more to share.
Best practices help us zoom out.
They guide this platform
transition, but they take us
into the future as our platforms
continue to grow and evolve.
First and most importantly, we
believe that you should build
one responsive website instead
of building parallel desktop and
mobile sites.
I know this is easier said than
done, but we really believe in
using this suite of responsive
design techniques to build a
single site.
And you should use feature
detection instead of user agent
sniffing.
In the past, some developers
have wanted to know the user
agent to specifically identify
iPad, but our new desktop user
agent on iPad will prevent you
from doing this.
I want to convince you that you
don't need to know your content
is on an iPad.
You just need to use feature
detection.
And the reason is that iPad is a
chameleon.
With all of the new content mode
APIs that Charles told you
about, your website could be in
app that is in desktop mode or
in mobile mode.
Your web content could be in a
Split View on either the mobile
side or the desktop side.
So knowing that your content is
on an iPad specifically is not
really that useful.
And the confusion with user
agent doesn't end with iPad.
Anything is possible.
We now have UIKit apps running
on the Mac.
Really, if you step back, we
have web content on Apple Watch,
on iPhone, on iPad and all of
the different content modes and
configurations we already
covered, web content and UIKit
apps on Mac, web content on
AppKit apps on Mac.
Targeting each one of these
configurations and handing it
big-screen site or small-screen
site is going to be so much more
limiting, more fragile and less
future proof than using feature
detection to see what each
configuration is capable of.
We're going to stand up here and
tell you that responsive design
is best practices for whatever
the hot new product is year
after year after year until
finally we don't have to because
it's the norm.
Server-delivered content that's
agnostic to who asked for it but
adapted with responsive design
is awesome on every device.
And this device landscape has
not been shrinking.
We know this takes a lot of
work, but we also know it's
worth it.
When we say iPad has
desktop-class browsing, we mean
modern desktop-class browsing,
and that means no plug-ins.
We've never had plug-ins on iOS,
and we don't intend to add them
now.
Even on Mac, if you go out of
your way to install Flash, it's
still off by default, and we're
dropping support for it in
Safari altogether in 2020.
So if you still haven't migrated
your old video or games or
restaurant menus to standard web
technologies, now is the time to
say goodbye to Flash.
Safari on iPad is desktop-class,
but iPad is still a mobile
device, which means that we
expect it to be used on the go,
often in public places.
Therefore, WebKit will prevent
audio from playing
automatically.
We have found some desktop
websites that assume that
automatic playback will happen,
but it's best not to assume.
The standard media API allows
you to know the truths of what
actually happened, because the
play function has a promise.
You should catch that promise in
case playback was rejected.
But you should also consider
designing your website so that
all users on all devices can
decide for themselves when they
want audio.
Next, think beyond the mouse and
hardware keyboard when designing
user interaction flows.
Specifically, consider using
pointer events, and avoid using
mouse hover for anything other
than decorative auxiliary
changes.
And finally, use built-in APIs.
This is a broad topic, but I do
have a specific example.
We found many desktop websites
that will use mouse events in
order to customize text
selection or text input.
But there's a better tool for
the job.
Selection change events and
input events are designed for
this exact purpose.
You'll always get the better
behavior on every platform if
you utilize built-in APIs
instead of reverse engineering
them with basic interaction
events.
So that's a lot.
To help you digest all of this
information, I'd like to invite
Wenson back to the stage so that
he can show you some of these
new features and best practices
in action.
Wenson.
[ Applause ]
>> All right.
Thanks, Beth.
So you'll never be able to
guess, but when I'm not working
on web browsers in my spare time
and not-so-spare time, I also
like to work on web
applications.
Today, I'd like to share one
I've been working on recently
called Shiny Sketch.
Now, I've only ever tested this
against desktop browsers, so
it'll be interesting to see how
it behaves on my iPad.
But first, let me give you a
quick tour on my Mac.
And as I do, I'll point out some
potential areas that we may need
to adjust in order to make it
look and feel great on iPad.
Okay, so here's what it looks
like on macOS.
And first, this website has a
fixed four-column layout.
What that means is as I make my
window smaller, I just get
horizontal scrolling, okay?
So that's one thing to keep in
mind.
The next is that in order to
access the Edit or Delete
controls, I have to hover over
each drawing.
Of course, the Edit and Delete
controls are really critical
part of my web application.
And lastly, I'm going to go in
and try to scribble on one of
these drawings.
So I'm able to use my trackpad
to draw here, but the code that
I'm currently using to implement
this only listens for mouse
events.
I'm probably going to need to
adjust that for compatibility
with iPad.
Okay, so that's Mac.
What does it look like on iPad?
Let me show you.
I happen to have the same page
open in Safari.
And the first thing I notice is
that the four-column layout
works great in landscape mode.
However, once I rotate into
portrait mode, let's see what
happens.
So I still get a four-column
layout.
I bet if I made my website
responsive, I could avoid this
scaling.
What I'm going to do now is
return to my Mac and see if I
can figure out what's triggering
this horizontal scrolling in my
browser.
Okay, so first things first.
I'm going to resize the window
so I do get horizontal scrolling
like this.
And then I'm going to go to the
Develop menu.
So I'm going to go down to Start
Element Selection, and that
allows me to see how big each
element is.
As an example, right here, this
Edit button is 72 by 48 pixels.
Okay, so I'm just going to start
at the top of the page and try
to find a really wide element.
So this Shiny Sketch page banner
or title is about 760 pixels.
If I look at the window width
itself using this ruler, it's
also about 760 pixels, so this
is pretty well proportioned.
I'm going to move my cursor
down, and as you might have
guessed, the gallery section is
a whopping 1,300 pixels.
So I'm pretty sure this is
what's triggering the horizontal
scrolling in my web app.
But I want to find out more it,
so I'm just going to click.
That's going to open Web
Inspector where I can see more
about the gallery.
So in the Styles sidebar, there
is indeed a hard-coded width of
1,300 pixels.
Why is this here?
Well, when I added this, I think
I was trying to make my website
look good on really large
displays, really wide displays.
I didn't think that anyone would
actually load my web page with a
display of less than 1,300
pixels width.
Maybe it was a reasonable
assumption to make at the time,
but now I think I can do better.
So what I'm going to do now is
switch over and try to fix it.
I'm going to make my website
responsive.
By the way, the entire web
application is just three files,
an HTML file, CSS file, and a
JavaScript file.
So I'm going to start in the
HTML file where I'm going to add
a meta viewport tag.
So there's two important
components to this
width=device-width and
initial-scale=1.0.
This lets the browser know that
the page is responsive at any
device width.
So that's important, but now
let's actually make it
responsive at any device width.
So let's go to our CSS file.
And remember that 1,300 pixel
rule we saw earlier?
I'm going to search for that.
So here we go, width: 1300px.
It is indeed on the gallery.
And what I'm going to do is just
change this to a max-width.
What that means is that if my
window is less than 1,300
pixels, the drawings under my
gallery should be able to
reflow.
They're able to reflow, because
I conveniently set them as
display: inline-block.
Okay. So those were a couple of
small changes.
Let's see how it now behaves.
All right, so it's now a
three-column layout in portrait
mode, but if I rotate back into
landscape mode, it becomes four
columns.
That's because landscape mode is
wide enough to accommodate for
the four-column layout.
Either way, there's no more
scaling, and that's great.
Okay, so next let's talk about
drawing.
As Beth mentioned, there's no
mouse move on iPad, but luckily,
I can adopt pointer events, and
the touch-action CSS property to
make it work.
So let's return to the CSS file.
So over here, I'm going to add
touch-action none, or
specifically, I'm going to add
it to the .drawable-canvas
which, as its name might
suggest, is the element that I'm
going to be dragging my finger
over as I draw.
Of course, it's important that
as I drag my finger over, it
doesn't scroll.
That's why touch-action is
important.
Okay, next I'm going to go over
to the JavaScript file where I'm
going to look for mousemove.
Okay, so this is the code that
I'm currently using to listen
for mouse events so I can draw
on my Mac.
I'm going to adjust this a
little bit.
So instead, what I'm going to do
is check if pointer events are
supported.
If they are, I'm going to
register pointer event listeners
instead.
Otherwise, I'll fall back to the
mouse events that I'm currently
using.
Okay, so let's see these changes
in action though.
Let's give our canine friend
Sona [phonetic] here a little
hat.
Well, it's not best drawn, but
as you can see, I can clearly
able to draw now.
Okay, so one last minor detail.
As Beth mentioned, iPad is
compatible with mouse hover, but
it's not immediately clear to me
that the way to edit or delete,
or drawings rather, is to first
tap on each drawing in order to
show the controls and then tap
on the controls themselves.
It means that any meaningful
interaction with my app is going
to require two taps on iPad.
Let's make it easy to edit and
delete with a single tap.
What I'm going to do is add a
Delete button that's always
visible and just make tapping on
each drawing immediately go into
editing mode.
So let's do that.
First, I'm going to go over to
the HTML file where I'm going to
add a bit of markup.
It's a very simple button that
I'm going to add here.
But note that I'm putting the
class static-control here.
It's going to become important
very soon, because I'm going to
head over to the CSS file where
I'm going to add a media query.
So what I'm doing here is I'm
checking if hover is supported
by using this media query, and
if it is supported, we don't
actually need that
static-control, that static
Delete button that I just added.
So I can just set display: none
on that, and it should hide it.
Lastly, there's a small change
we have to make to our
JavaScript file.
So now I'm going to look for
mouseenter, and this is a code
I'm using to show or hide the
hover controls when the user's
mouse enters or leaves each
drawing.
We don't need to do this if
hover is not supported.
So what I'm going to do is add a
bit of logic up here.
It's going to use the same media
query as I did in the CSS file
to check if hover exists, and if
hover is not supported, I'm
going to instead add a click
event listener to the drawing
itself and allow it to
immediately start drawing mode.
Then this early return ensures
that I don't add the event
listeners I don't need in the
case where hover is not
supported.
Okay, so that was a lot of
changes.
But let's see it in action now.
All right, first thing I notice
is that Delete buttons are now
always visible and ready to be
tapped.
But if I tap on each of the
drawings, I'm able to edit them
immediately.
So it's one tap to do anything
in my app.
Okay, so with a few simple
steps, I've made my desktop web
app work great on iPad, and I
think you web developers out
there can as well.
I'd like to now hand it off to
Charles to summarize what we've
covered today, and I'll see you
at the labs.
[ Applause ]
>> Thanks, Wenson.
Your web app looks awesome.
In summary, iPad has made major
new advancements to make today's
desktop websites work great out
of the box.
We want you to take advantage of
these new features to make your
app or website even better on
iPad just like you did for
iPhone.
For our web developers, consider
building one responsive website
using the techniques we talked
about today.
For app developers, let Safari
View Controller and
ASWebAuthenticationSession do
the heavy lifting for you.
If using WKWebView, please test
your app.
And please file bugs and send us
your feedback.
We'd love to hear from you.
For more information about our
other exciting authentication
and web platform features, you
should definitely check out
Session 504, What's New in
Authentication, Safari, and
WebKit on Thursday at 11:00 am.
There are also two labs related
to the session today at 1:00 pm
and Thursday at 12:00 pm.
Please come to talk and learn
more about what you heard today.
Thank you and have a wonderful
WWDC.
[ Applause ]