WWDC2019 Session 712

Transcript

[ Music ]
[ Applause ]
>> Welcome to Advances in
Networking.
My name is Joshua Graessley.
My coworkers and I from Internet
Technologies are excited to talk
to you about a number of
improvements we've made in the
area of networking on our
platforms this last year.
In part one, we're going to talk
about Low Data Mode.
This is an exciting new feature
where we can work together to
help save our user's network
data when it really matters.
We'll talk about how you can
write asynchronous-- very
elegant asynchronous networking
code using Combine in
URLSession.
We'll also cover WebSocket APIs.
Last year, this was one of the
number one things you requested
in the labs and we're excited
this year to be providing a
solution.
Finally, we'll talk about a
number of mobility improvements
and we'll talk about how we can
work together to provide our
users a more seamless experience
as devices move from one network
to another.
In part two, we'll cover a bunch
of additional topics.
That's at 5:00 today.
I encourage you to come back and
check that out.
Now, before we dive into Low
Data Mode, I want to take a
minute to talk about networking
APIs on our platform.
The best networking APIs to use
on our platform are URLSession
and Network.framework.
As you'll hear about in this
session and in part two, there
are a number of great
improvements we've made and all
of those improvements are
available to your applications
if you're using these
frameworks.
If you're using sockets, you're
not going to be able to take
advantage of this.
Unfortunately, sockets just
doesn't have the richness that
we require in order to provide
this functionality at that
layer.
Now, if you're doing something
like a VPN or a content filter,
NetworkExtension framework is a
great solution and we've made a
number of improvements there as
well that we'll be talking about
tomorrow at 9:00 a.m. Now with
that, I'd like to talk about Low
Data Mode.
Low Data Mode is a really
exciting new feature in iOS 13.
I think the best way to
introduce Low Data Mode is to
have you think back to how you
got here to WWDC.
You probably got on an airplane
to fly out to sunny San Jose.
And on that airplane, there was
probably a Wi-Fi network and
that Wi-Fi network was probably
kind of expensive of it may have
been very congested and you may
have been finding yourself
wishing that you had some way to
tell the applications and the
system on your device that on
this particular network it's
really important to be very
conservative in how you use
network data.
Low Data is all about solving
that problem.
Low Data gives users an
ability-- a preference to signal
to your application and to the
system that you really want to
be very careful in how you use
data on this network.
This can be set on a per Wi-Fi
network based on SSID and a per
cellular network based on SIM.
When the device is on a Low Data
Mode network, this has two
primary impacts.
There's a change to the system
policy and a change to your
applications that have adopted
Low Data Mode.
For the system policy, we will
defer any background
discretionary tasks while we're
on a Low Data Mode network.
So, if you were on your airplane
and you had Low Data Mode turned
on, we would defer all of those
background discretionary tasks.
And when you get off the
airplane and check into your
hotel and you get onto the Wi-Fi
network there that's potentially
less congested or less
expensive, then we'd let all
those background tasks resume
and carry on.
Another change we make is that
Background App Refresh is
disabled.
This helps us avoid allowing
applications that are in the
background that may have nothing
to do with what the user is
actually interested in from
consuming network data.
This gets us some pretty big
wins, but we think some of the
biggest wins are going to come
from the changes that you're
going to make in your
applications to adopt Low Data
Mode.
So, I'd like to spend some time
talking about the techniques
that you can use.
It all starts with looking into
how your application is actually
using network data.
As you start to dig in, it's
really important to keep in mind
any time you can save data and
there's no impact on the user
experience, you should always do
that.
I know this seems kind of
obvious, but you might be
surprised about the ways that
you can make optimizations that
will reduce the amount of
network data that you're using.
Now, once you've gotten through
all of those low-hanging fruits
where you can just get a win
with no impact, then you're
going to have to start making
some decisions about tradeoffs.
In a lot of cases, you have the
opportunity to use some more
data and give a really great
experience or use a little bit
less data and give a still great
but maybe not quite as great
experience.
And Low Data Mode is all about a
way for the user to signal to
your application that they'd
really prefer that you save that
data and go with the option that
still gives a great experience,
but maybe not quite as great.
So, let's talk about some
techniques you can use.
The first one is to reduce image
quality.
If your application isn't all
about images, by reducing image
quality, you can save a lot of
data and you can still let the
user do what they're trying to
do, but save data in the
process.
You can reduce pre-fetching.
Prefetching is a great technique
for improving performance, but
it has this downside that you
may end up fetching resources
that the user never needs.
And in the event that they're
really concerned about how much
network data they're using,
pre-fetching can actually be
counterproductive.
So, when you're in Low Data
Mode, you can use-- you can
eliminate your pre-fetching, you
can save that data.
And the user may have to wait a
little bit longer if they scroll
that content into view to wait
for it to load.
You can synchronize less often.
The data will be stale for a
little bit longer, but you'll
still have data.
The user can still accomplish
what they've set out to do, but
over a long period of time you
can actually get some pretty big
savings through reduced rate of
synchronization.
You can mark background tasks as
discretionary.
You'd be surprised how many of
the background tasks you're
setting up don't really need to
be done immediately.
By marking a background task as
discretionary, it gives the
system a lot of flexibility in
when it actually schedules that
operation.
As I had mentioned in--
previously-- when you're on the
airplane, that gives the system
the opportunity to put off that
task until we get to a non-Low
Data Mode network.
Another great solution is to
disable auto-play.
This is really great because it
doesn't prevent the user from
playing the content that they're
interested in, but it means the
user doesn't have to pay for
content that they really don't
care about.
That brings me to another really
important point when you're
choosing how you're going to
implement Low Data Mode.
Please don't block
user-initiated work.
Low Data Mode is all about
telling the system to reduce the
amount of network data that it
uses, but to make sure that the
user can still accomplish what
they're setting out to do.
As you're going through your
application, you may realize
that some of the operations you
do do take a lot of network data
and there's nothing you can do
about that.
It's great to reduce the amount
of network data you're using for
those operations any way you
can, but we really don't want
you to pop up a dialog that says
are you sure you wanted to do
this.
I see you're in network data and
this is going to use a lot of
data.
Don't second-guess the user.
They've turned on Low Data Mode
and they've asked your
application to perform that
operation.
Go ahead and do that.
So, let's talk a little bit
about the APIs that your
application can use to implement
Low Data Mode.
We've added APIs to URLSession
and Network.framework.
The thing to keep in mind is
that when a network is in Low
Data Mode, the constrained
property is going to be set on
that network.
So, all of the APIs that we
provide are based on the
constrained property.
In URLSession, we've added a
property called
allowsConstrainedNetworkAccess.
By default, this is set to true.
Your applications are allowed to
use Low Data Mode networks by
default.
You can set this to false to opt
out.
You can set this on URLSession
requests as well as your
URLSession configuration.
With URLSession, we really
encourage you to go ahead and
try that large resource fetch or
pre-fetch and set
allowsConstrainedNetworkAccess
to false.
If you get a failure and the
error has a network unavailable
reason of constrained, that
indicates the operation failed
because you're in Low Data Mode
and the right thing to do there
is to turn around and perform
your Low Data Mode operation.
In the case of a large resource
fetch, that would be fetching a
smaller resource instead.
In the case of a pre-fetch, just
wait until the user actually
needs the content.
This has the added benefit of
letting you take advantage of
anything that may actually
already be in the cache.
Now, with Network.framework, you
have a similar property called
prohibitConstrainedPaths.
You can set this to true to
block your connections and other
networking objects from using
Low Data Mode networks.
With Network.framework you have
another option, though.
If you're going to connect to
the same host whether you're in
Low Data Mode or not, you can go
ahead and establish that
connection.
And once the connection is
established, you can get the
current path and on the current
path you can check to see if
it's constrained and this will
tell you whether or not this
connection is established over a
Low Data Mode network.
Now, if you go this route, it's
really important to make sure
that you handle path updates.
It's very possible that the
constrained property can change
over the lifetime of your
connection.
So, I spent a lot of time
talking about the constrained
property and Low Data Mode.
There are a few other properties
that your application can use to
make decisions about what to do
on a given network.
There is the expensive property,
which we introduced in
Network.framework last year and
we're bringing to URLSession
with the
allowsExpensiveNetworkAccess
property this year.
There's also checks for specific
interface types, such as
Cellular or Wi-Fi.
If you haven't adopted any of
these things yet, you're in a
great position to focus on
adopting Low Data Mode and
that's definitely the way to go.
With Low Data Mode, which
correlates to the constrained
property, the user has control.
They have a setting they control
that with.
Expensive, on the other hand, is
a property that's set by the
system and it's almost always
set for cellular networks and
it's also set for Wi-Fi networks
when they're associated with a
Personal Hotspot.
You can also check for cellular,
but that also is something that
the user has no control over.
So, if you're currently doing
any checks based on cellular
interfaces or expensive
properties, it's a really great
idea to move over to doing-- to
using constrained and taking
advantage of Low Data Mode.
If you've looked at these
options and you've decided that
you still want to be making
decisions based on whether
something's expensive or
cellular, we strongly encourage
you to use expensive.
Expensive is a lot more flexible
and in many ways it effectively
futureproofs your application.
Right now, cellular networks are
almost always marked as
expensive, but they may not be
in the future and there are
other interface types that may
come around that may also be
expensive.
By using the expensive property,
your application is going to do
the right thing in the future
with different interfaces.
If you're checking for a
specific interface such as
cellular, you're not going to be
able to take advantage of that.
I'm really excited to see what
you guys do adopting Low Data
Mode.
Thank you so much for your time.
With that, I'd like to have
Guoye come up to talk about
Combine in URLSession.
[ Applause ]
>> Thank you, Josh.
Good morning.
I'm Guoye Zhang.
I'm excited to tell you today
about how we are supporting a
new Swift framework, Combine in
URLSession, and how you can use
Combine to streamline your
networking code.
Combine brings decorative
asynchronous programming to
Swift.
To explain what it is, let me
start with an example of
building a responsive search
field.
So, search field publishes
values whenever user types and
sink here subscribes and
receives a URL to start a
search.
In-between, we use the map
operator to map the value into a
URL.
Now, let's suppose I only want
to start a search when there is
enough content.
We can use the filter operator.
The filter operator in this case
drops all strings shorter than
three characters.
Now we've eliminated the user's
queries like a single H.
However, the search still
happens too frequently.
What if I only want to search
when user stops typing for a
moment.
We can use debounce operator.
[ Applause ]
So, debounce delays the value
and only forwards it when there
is a significant delay, in this
case 0.2 seconds.
However, with debounce, if user
types something and deletes it,
we might end up sending the same
value down the chain and
starting the same search over
and over again.
To solve this, we can add the
removeDuplicates operator.
removeDuplicates remembers the
last value received and only
forwards a new value when it
changes.
Now we have the final version of
the search field by changing
these powerful operators, your
asynchronous code be linear and
composable.
Combine processes values over
time.
It consists of publishers,
operators, and subscribers.
The chain is driven by the
request sent from the
subscriber.
In response to the request,
publisher sends value down the
chain.
This is how that pressure is
handled by Combine.
If you want to learn more,
Introducing Combine session
video will be available shortly
and I encourage you to attend
Combine in Practice sessions
this afternoon.
Networking is inherently
asynchronous, that's why it's
perfect to adopt Combine.
This year, we're introducing
DataTaskPublisher in URLSession.
It is a single value publisher
and it works similarly to our
existing closure based
convenience methods, which means
you can create it from a shared
URLSession or from your own
URLSession and receive
authentication challenges and
metrics on your delegate.
This is the interface of
DataTaskPublisher.
It conforms to the publisher
protocol.
On success, it sends you a true
pool of data and response.
On failure, it sends you an URL
error.
Now, let me give you a demo on
how Combine works in URLSession.
So, for the purpose of this
demo, I've disabled URL cache,
so all the resources to fetch
over the network.
I've also used Network Link
Conditioner to simulate a
realistic 3G environment.
So, I am building this for a bar
called PubSocket.
This app shows name, image, and
item price of each item in the
bar.
So, after listening to Josh's
talk about Low Data Mode, I've
decided to provide a
high-resolution image and a
low-resolution image for Low
Data Mode.
Right now I'm in Low Data Mode,
so you see these black and white
images.
And if I switch off Low Data
Mode, these images are replaced
with the high-resolution
variant.
Let's see how this is currently
implemented without Combine.
So the interface is built in
UITableView and here we have the
data source method cellForRowAt
indexPath.
In this method, we dequeue a
reusable cell and configure the
name and price of each item on
the cell.
Then we start a URL request to
fetch the high-resolution image
and disable constrained network
access.
pubSession here is the shared
global session we use in
PubSocket app and we use the
pubSession to create a data task
from the request.
When task finishes, we check to
see if the status code is 200.
OK. We convert the data into the
image and put the image on the
cell.
When task fails due to Low Data
Mode, we create a new data task
to fetch the low-resolution
image.
And we do the same thing here.
We check the status code, we
convert the image, and put it on
the cell.
Don't forget to resume these
tasks.
So, as a networking engineer, I
know the networking logic here
is solid.
I'm adhering to the best
practice of not doing any
pre-fetch acts.
However, I'm not very happy with
the current code because it has
lots of duplications.
We are checking the status code,
converting the image twice.
Also, you might have noticed
I've made the very common
mistake of capturing the cell
and putting the image on the
cell asynchronously.
The cell could have already been
reused at this time by UIKit.
So, I'm going to show you the
bug.
I'm going to scroll down very
quickly.
Please pay attention to the last
few items on the menu.
As you can see, hot dog and
drumstick have the wrong image
being placed.
Let me do it again.
I'm going to scroll up to the
top.
Please pay attention to the
first few items.
Yeah, root beer and fries have
the wrong image being placed on
them before they are replaced
with the correct one.
Now let's see how we can use
Combine to fix all these issues.
First, let me delete this code
to fetch.
Here we have the
MenuItemTableViewCell class
here.
So, cell receives an image, so
it's a good place to put a
subscriber on it.
The subscriber here conforms to
any cancelable protocol.
We can cancel the subscriber in
prepare for reuse method.
The cancellation happens
immediately, which means we
won't ever get the chance of any
image being placed on the wrong
cell.
Now let's go back to the
TableView data source method,
cellForRowAt indexPath.
We start by doing the same
thing, create a URL request and
fetch the high-resolution image
and disable constrained network
access.
But instead of a data task, we
create a data task publisher for
the request.
Then we use the new tryCatch
operator in Combine.
The tryCatch operator catches
the error produced by
DataTaskPublisher and if the
task failed because of Low Data
Mode, we replace the publisher
with the new publisher to fetch
the low-resolution image.
Otherwise, we just rethrow the
same error down the chain.
Next, let's use tryMap operator
to handle the success case where
we receive the data and the
response.
We check the status code and
create an image from the data.
This map handles both
high-resolution image and
low-resolution image,
eliminating the code
duplication.
Finally, we replace the error
with a placeholder image, switch
the main queue, and use the
assigned subscriber to put the
image on the cell.
Now, this is pretty good.
We've implemented the same logic
with much shorter code and
linear code.
But can we do more?
There is one more operator I
want to show you.
It is called retry.
Imagine what you have to-- what
you have to do before to support
retry.
You have to either call the data
task creator recursively or
maintain a state machine.
Now in Combine, I can just put
retry operator right here before
we replace the error.
[ Applause ]
So, retry catches the errors
thrown here and it retries by
restarting the chain of
operators and fetches the image
again.
In this case, I'm just retrying
once.
So, networking APIs on all
platforms are designed to be
very reliable, so in general you
don't need to retry.
However, your app might need to
connect with some flaky server
or meta boxes that gives you 500
server errors frequently.
In this case, this tryMap
operator will throw an invalid
server response error, which can
be caught by retry.
But please be aware that
networking operation is very
expensive; retry is no
exception, so avoid retry if
possible.
If you have to retry, start with
a very low number.
Also, please pay attention to
the idempotence of your request.
In my app, downloading an image
twice is fine, but if your app
handles transaction like
payment, blindly retrying could
be very dangerous.
Now let me turn on Low Data Mode
again.
And let's see the app running
again.
As you can see, we fetched the
low-resolution images just like
before.
And if I turn off Low Data Mode,
the same high-resolution images
and we won't get any wrong
images being placed on the cell.
[ Applause ]
OK. Let's go back to the slides.
To recap, I've just shown you
how Combine can make your
networking code concise, linear,
and less error-prone.
I've also shown you how
composable these Combine
operators are, that you can
support retry by just adding
one-off code.
But please pay attention to use
low retry count and only retry
idempotent requests.
Finally, I've shown you how you
can use Combine with Low Data
Mode without doing any pre-fetch
checks.
Here is the code I extracted
from my demo to do adaptive
loading for Low Data Mode.
It takes a regular URL and a low
data URL and gives you back a
publisher of data.
First, we create a-- we create a
request to fetch the regular URL
and disable constrained network
access.
We use the URL-- we use the
request to create a data task
publisher.
Then, immediately then we handle
the error caused by Low Data
Mode and then we replace the
publisher with a new publisher
to fetch the low data URL.
Next, we handle both success
cases together, checking the
status code and give the data
back to you.
You can use this code as a
starting point of Combine and
Low Data Mode and customize this
code to your needs.
I have to mention that some of
the APIs are not available yet
in current SDK.
We are working on getting them
into a future beta.
Next, I want to invite my
colleague Jiten to talk about
WebSocket.
>> Thank you, Guoye.
Good morning everyone.
My name is Jiten Mehta and I'm
excited to talk to you about the
WebSocket protocol in Apple's
networking frameworks new in iOS
13 and macOS Catalina.
In the past years, a large
number of developers have asked
us for the WebSocket protocol
support in Apple's frameworks.
In fact, it was the number one
developer request from a
networking survey that we
conducted last year.
WebSocket allows bidirectional
communication over a single HTTP
connection.
This enables developers to write
applications like chat,
multiplayer games, and other
real-time applications.
WebSocket works over the
well-known HTTP ports and is
fully compatible with the
existing web infrastructure,
allowing you to connect to
proxies, CDNs, and firewalls.
Historically, the WebSocket
protocol has been available as a
JavaScript API in web browsers,
but looking at the benefits that
WebSocket brings to web apps,
we've decided to extend this API
to our networking framework in
addition to the existing
JavaScript API already available
in web views.
This enables you to use your
existing web infrastructure and
bring it to your native apps on
Apple's platforms.
Before we talk about WebSocket,
let's take a look at a common
technique used today to enable
bidirectional communication.
Let's take a chat application as
an example.
When a client wants to receive a
response from the server, it
sends out a request.
The server responds with a 200
status code immediately, but it
does not send out the response
body because it doesn't have one
at this point.
Sometime in the future, once the
server has a response ready for
the client, it sends it out to
the client.
At which point, the client sends
a new request, indicating that
it wants to receive the next
message.
This is known as long polling,
but there are some disadvantages
associated with long polling.
Both the end points when they
want to send messages have to
either send an HTTP request or
an HTTP response, which is a lot
of overhead.
Additionally, complexity has to
be maintained at the server to
enable long polling.
Let's see how WebSockets can
solve this problem for us.
As the first part of the first
step of the WebSocket handshake,
the client sends out a request
to the server, indicating that
it wants to upgrade this
connection to WebSocket.
The server responds with the 101
switching protocol to response,
at which point we have a
bidirectional stream between the
two end points.
Both the end points are now free
to send messages in either
direction.
They can send messages like data
string or ping and pong frames
without any HTTP overhead.
URLSession is Apple's
recommended API for HTTP.
And this year we are excited to
announce
URLSessionWebSocketTask, a new
API in the foundation framework.
[ Cheering and Applause ]
To create a WebSocket task, you
can simply pass in the URL that
you want to connect to and call
resume.
We will start the handshake and
you need not worry about
handling any of the status
codes.
The first part of the WebSocket
handshake uses HTTP semantics,
which means your
URLSessionWebSocketTask will use
your existing URLSession
configuration objects.
It will also use your network
storages to do cookie and
convention look ups and we will
honor your delegates for any
challenges.
Once you're connected, you can
send data or string messages on
the task.
You can also receive messages on
the task by passing a completion
handler, which will be called
asynchronously once we receive
the entire message from the
server.
The URLSession API for
WebSockets is closer to the
JavaScript API, which is based
around complete messages and
callbacks.
But some developers need more
than this, like sever support or
reading partial messages.
And for that, we are excited to
announce WebSocketSupport in
Network.framework through the
NWConnection and NWListener
objects that give you both
client and server support.
[ Applause ]
With this, you can have a
message oriented transport
protocol which can be extended
for peer-to-peer communication.
You can also receive partial
messages by specifying the
minimum and maximum bytes for a
give and receive operation.
To add WebSockets to your
network frame of objects, you
can simply create a parameters
object with TLS enabled on it.
Next, create a websocketOptions
and set it on the default
protocol stack of the
parameters.
Once you create the parameters,
next you can pass in these
parameters to the NWConnection
constructor to create an
NWConnection object.
Or if you're looking to create a
listener, just pass these
parameters to the constructor of
the listener.
The send and receive APIs remain
unchanged from last year and you
can continue using those to send
and receive WebSocket messages.
Now, let's take a look at
WebSockets in action.
I'm going to build upon the
application that Guoye just
showed you, PubSocket, but I
want to change the business
model of PubSocket slightly.
The price of items is now going
to be dynamic and will change on
demand.
So, think about a stock market,
but for food and drinks.
So, on the left-hand side you
see PubServer, which is the
application that the bartender
sees where they can edit the
items or make changes to the
price.
On the right-hand side is the
pub menu that we've already
seen, which is the application
that your clients or customers
walking into the bar can see.
The new feature with dynamic
pricing is going to be called
PubSocket+.
So, see-- let's see how
PubSocket+ works with our
current implementation of server
and client.
Let's suppose the bartender
wants to bump the price of root
beer to $6.99.
I click update.
And now the client has to pull
down to refresh.
And once they do that, they get
the updated price of root beer.
That's OK, but I'm sure we could
do better than this.
I want customers of PubSocket+
to have a seamless experience
where they don't have to pull
down to refresh and they can get
the price changes live.
Let's see how WebSockets can
help us achieve this.
Let's head over to Xcode and
first I'm going to stop the
server and the client.
And let's head over to our
server where I have an
NWListener which is acting as my
TCB server.
Here I have some parameters that
I've created with TLS options
set on them.
First, I'm going to make a
change here to create some
WebSocket options and set it on
the protocol stack of the
parameters.
This tells my server how to do a
WebSocket handshake with clients
that are trying to connect with
it.
The next change I would make on
my server is in this function
called sendPriceChanges.
This function is meant to send
out WebSocket messages to all
the clients that are connected
to it each time a price of an
item changes on the server.
Currently, I have a send
implemented with default stream
context.
This means that the data that I
pass to the send method is sent
out as a bag of bytes on this
TCB connection and it does not
have any message framing.
I'm going to change the context
here and create a new context
with some WebSocket metadata
associated with it.
Now, this tells my connection to
send the data as WebSocket
message frames.
With these two changes, my
server should be all set to send
out WebSocket messages to my
client.
Now, let's see-- I can keep the
server building while we head
over to the client.
So, on the client side, I'm
going to make changes to the
function connect first; connect
is going to connect to a new
server and for that I'm going to
use URLSessionWebSocketTask I'll
simply pass in the URL to the
task and then call resume so I
can start doing the handshake.
Once I'm connected, I'm going to
call readMessage, which will
receive a message back from the
server.
Now, let's see how readMessage
should be implemented.
Inside readMessage, I'll call
receive on the task and pass a
completion block.
On the success case, I'll update
my UI with the price change and
immediately after that I'll call
readMessage again so I can read
the next message coming back
from the server.
Once I make these two changes on
the client, I should be all set
to connect to my server and
receive WebSocket messages.
So, let's run a new server and a
client to see how they work.
First I'm going to run my
server.
And now our new client reads
PubSocket+, which has the new
and improved WebSocket ability.
Now, let's suppose it's happy
hour and the bartender wants to
reduce the price of root beer
down to $1.99.
So, let me make that change.
And I'm going to click update
and then you can see on the
client the price got updated
without the client having to--
[ Applause ]
So, for those of you who missed
it, I'm going to reduce the
fries to go with that root
beer-- now that's some happy
hour.
And once I click update, you'll
see the price of fries change on
the client without me having to
pull down.
And I'll do it once again and
there the price of fries
changes.
[ Applause ]
So, that is WebSockets
bidirectional communication
without any HTTP overhead.
Some of you might be wondering
what the Stats button on the top
right is for.
If I click that, here are some
new statistics that we are
collecting through our
URLSession metrics API.
Additionally, at the bottom, RTT
is the round-trip time between
my client and my server that I'm
calculating using pings and
pongs in WebSockets.
I'm currently using Network Link
Conditioner to simulate a busy
bar environment.
You could use something like
this to monitor the health of
your connection between your
client and server.
If you are interested in knowing
more about the new properties
added to the metrics API or how
to use Network Link Conditioner,
please join us for the second
networking session at 5:00 p.m.
today.
Now, let's head back to our
slides.
Let's do a quick recap of
PubSocket+.
For our server, we used
NWListener with WebSocket
options set on the protocol
stack.
For a client, we used
URLSessionWebSocketTask to
connect to our server and read
messages.
For our transport, we used
bidirectional WebSocket
messages.
And finally, the advantage of
doing everything was
bidirectional messaging with
very little HTTP overhead.
Let's review the APIs available
for you to add WebSockets to
your apps today.
WebKit gives you the ability to
add WebSockets with the existing
JavaScript API in your web apps
and web views.
New this year,
URLSessionWebSocketTask built on
top of Network.framework plugs
into URLSession.
It works with your existing
URLSession configuration objects
and offers automatic cookie and
authentication support.
It also offers a convenient way
to measure round-trip time using
ping and pong handling.
Also new this year,
WebSocketSupport in
Network.framework through the
NWConnection and NWListener
objects give you both client and
server support.
It gives you direct access to
complete and partial messages,
including ping and pong frames.
You can optionally set custom
headers like cookies or other
authentication headers through
the websocketOptions object.
We are happy to be opening up
this technology to all
developers and we're excited to
see what you do with this.
Next, I would like to invite
Christoph to talk about mobility
improvements.
[ Applause ]
>> Thank you, Jiten.
Hello everyone.
I am Christoph and I am going to
show you what mobility
improvements we did in iOS 13.
So, users are often experiencing
this.
When they are walking out of
their home, when they are far
away from their Wi-Fi access
point, the signal of Wi-Fi gets
worse and often the applications
become slower because the
networking becomes slower as
well.
Sometimes the applications
completely fail.
And so people have gotten used
to this that when they are
walking out of their home they
just swipe up to the Control
Center and turn off Wi-Fi.
Now, I am sure we have all
experienced this, right?
So, we want to change this.
We believe that users should
never have to turn off Wi-Fi
when they are walking out of
their home.
We believe that your
applications should just work
even when Wi-Fi is in a very bad
condition.
And I'm sure everybody here in
this room wants to achieve the
same goal.
So, let me show you how we can
get there.
This is the way we usually
represent Wi-Fi.
We have the Wi-Fi access point
in the middle, concentric
circles around it that show how
the Wi-Fi signal is gradually
fading away, getting weaker and
weaker as the phone gets farther
and farther away.
In this kind of scenario, it
would be very simple for a phone
to decide whether to use Wi-Fi
or switch over to cell.
Right? Now, the problem is that
this kind of representation of
how Wi-Fi looks is actually very
far away from reality.
In reality, it looks more like
this.
You have the Wi-Fi access point
in the middle and then a very
spotty Wi-Fi signal around it.
And the spottiness is because of
the objects in the room are
interfering with the signal.
The house, the walls, everything
is making the Wi-Fi signal a
very uncertain indicator of the
quality.
And a tiny little movement of
the phone could move the phone
from a good position to a bad
position.
So, for the phone, it's really
difficult to know whether Wi-Fi
is still good or bad.
It might still receive the
beacons from the access point,
but the signal might
nevertheless be too low to
actually send or receive any
data.
So, in this kind of environment,
the phone needs to decide
whether to use Wi-Fi or whether
to use cell.
It's this uncertainty around the
Wi-Fi signal that is the whole
challenge around mobility.
So, at Apple, we have been aware
of this problem for quite a
while and I will show you how in
the past we have done many steps
to improve this kind of
scenario.
All of it started back in iOS 7
with Siri.
In iOS 7, we introduced
Multipath TCP for Siri.
Multipath TCP allows to use
Wi-Fi and cell at the same time.
So, starting in iOS 7, whenever
people are using Siri and
walking out of their home,
Multipath TCP will make sure
that the traffic goes either
Wi-Fi over cell, reducing the
latency for Siri users and
reducing the error rates.
We are seeing great results
thank to Multipath TCP.
Now, a real end-to-end multipath
protocol, like Multipath TCP,
requires both the client and the
server to be aware of it.
Both need to work together,
right?
They need to work together to
decide whether to send the
traffic on Wi-Fi or on cell.
And so we asked ourselves how
can we improve mobility without
having both the client and the
server have to work together
without requiring the need to
modify the server
configurations.
And the answer came two years
later in iOS 9 with Wi-Fi
Assist.
Wi-Fi Assist handles mobility
for all applications, for all
flows, talking to any server.
And the way it does it is by
first starting on Wi-Fi and when
the signal is bad and the
connection doesn't, manages to
get established quickly enough,
we will just raise another
connection over the cellular
link.
Since iOS 9, since we are--
since we introduced Wi-Fi
Assist, all of your applications
that are using the high-level
APIs can benefit from Wi-Fi
Assist and are having a much
better experience when the user
is mobile.
And this works for any server
talking to any servers on the
internet.
Now, Wi-Fi Assist might still
get stuck on Wi-Fi, if for
example the connection managed
to get established, but then the
signal degraded afterwards.
And those flows would then still
be stuck.
In order to handle those kind of
scenarios better, one still
needs real end-to-end multipath,
the same way as we have it for
Siri.
So, after four years of
experience with Multipath TCP
for Siri, we decided to open up
the API for every one of you.
So, since iOS 11, you can start
using the handover or
interactive mode in URLSession
or the Network.framework.
So, when you are able to make
sure that your servers are
ready, you can enable Multipath
TCP and get the same benefits as
Siri.
So, in each of those releases,
iOS 7, iOS 9, iOS 11, we focused
on one specific area to improve
mobility.
We focused on Multipath TCP, we
focused on Siri, we focused on
Wi-Fi Assist.
And now comes iOS 13.
And in iOS 13, we improved so
many things they don't even fit
on this slide anymore.
In iOS 13, the mobility
improvements-- thank you.
[ Applause ]
Thank you.
In iOS 13, the mobility
improvements go throughout the
whole system.
Many different frameworks,
daemons, applications, from the
firmware to the driver.
Everything is now improved for
mobility.
And in this part of the session,
I am going to talk about two of
them; Wi-Fi Assist and Multipath
Transports.
So, first up, Wi-Fi Assist.
Traditionally, Wi-Fi assist has
only been taking very limited
amount of information into
account to decide whether Wi-Fi
is good enough.
Now in iOS 13, we changed that.
We made it such that all
components in the system are
providing information into Wi-Fi
Assist so that Wi-Fi Assist has
a full cross-layer mobility
detection.
The lower layers, Wi-Fi and
cell, are providing information
about the signal quality in a
much fine-- more fine-grained
way than in iOS 12.
And also, the higher-layer
frameworks like
Network.framework, URLSession,
other daemons of the system,
they are all providing
information into Wi-Fi Assist
about how their flows are making
progress.
All of this information going
into Wi-Fi Assist will then
allow it to detect whether we
are in a mobility scenario or
not and whether we should maybe
start trying to use cell.
So, all of this information,
then Wi-Fi Assist is going to
take its decisions and feeds it
back into the system.
It tells the lower layers, Wi-Fi
and cell, to make efforts to
improve the signal quality.
And it also communicates to the
other layers like
Network.framework and URLSession
to start recovering flows.
All of this leads to a much
improved flow recovery.
Now, even when a flow has
already been established on
Wi-Fi and has started to
exchange data, if later on the
signal quality is reducing, we
are able to move the next
request that would have been
used on Wi-Fi, we are able to
move that one over to cell.
So, your applications should now
much less be getting stuck on
Wi-Fi.
Now, the question for you, of
course, is how can you get the
benefits from Wi-Fi Assist?
How can you get the benefits
from all the improvements that
we did in iOS 13?
So, first of all, the way you
can get them is by using the
high-level APIs like URLSession
and Network.framework.
All of these APIs have been
built with Wi-Fi Assist in mind
and they are getting the full
benefit from it.
So, make sure that your
applications are using those
APIs.
Next up, some of you are doing
active interface management with
APIs like SCNetworkReachability.
You are maybe doing pre-flight
checks to know where is your
request going to end up on.
Is it going to go on Wi-Fi or on
cell?
The problem with those
pre-flight checks is that when
you do it and the moment when
you actually use the connection,
the interface might have
changed.
Wi-Fi Assist might have decided
to actually send your flow over
to cell or Wi-Fi might have
significantly improved.
So, the pre-flight check is a
very bad indicator of where your
flow is going to end up on.
So, we encourage you to rethink
your usage of the pre-flight
checks and come talk to us later
in the labs about why you need
it and we will work with you to
find an alternative.
Now, if you still need to steer
flows, for example, away from
cell because, for example, the
data transfer is way too big or
the traffic is not critical for
the user experience, you can use
helpers like
allowsExpensiveNetworkAccess and
set them to false.
That way, your request is not
going to go on the cellular
link.
So, this is Wi-Fi Assist and we
made a lot of efforts to make it
even better in iOS 13 and you
can get the benefits by using
the high-level APIs.
This brings me to the next one,
which is Multipath Transports.
Siri has been using it for quite
a while now.
We have opened up the API two
years ago and we have encouraged
you to look into your apps and
see which of your flows might
benefit the most from Multipath
TCP.
So, this year in iOS 13, we ran
this well and looked into our
own apps and decided to see
which one is going to benefit
from Multipath TCP.
Which one is often used when
mobile and has a very critical
flow for the user experience
that is hard to recover?
Well, one application is Apple
Maps.
Most users, well, when they are
getting their directions, they
are walking out of their home
and they are using search.
So, in iOS 13, we enabled
Multipath TCP for Apple Maps.
[ Applause ]
Now, whenever you are walking
out of the home and using Maps,
looking up your directions,
trying to search a restaurant,
MP TCP will be used and move
your flow over from Wi-Fi to
cell.
And since Monday, since the
first beta, we have been
enabling it and we are going to
see a much better responsiveness
for Apple Maps.
The next one where we are having
a very critical user experience
and that is also often used when
walking out of the home is when
you are streaming music.
Right? When you are streaming
music, you are downloading a
large file full of music content
and you don't want this music to
stall.
Because when it stalls, the user
will be disrupted.
So, since iOS 13, we enabled
Multipath TCP for Apple Music.
[ Applause ]
We are seeing much less music
streaming stalls because MP TCP
is now moving flows over to cell
whenever we are getting close to
stalling.
And so the user is going to have
a much better experience.
Now, the thing about this, we
have enabled it for Siri, Maps,
and Music, and you can do this
too.
We recommend-- we encourage you
to look into your applications.
Which one is often used when
walking out of the home?
Which one has a very critical
flow that is critical for the
user experience and that is hard
to recover?
Those kind of flows, they are
perfect for the multipath
service type and you can choose
them on handover or interactive
in the URLSession and
Network.framework.
Now, don't forget, if you do
enter in multipath, it still
requires both the client and the
server to work together, so go
and visit this URL to make sure
that your servers are configured
correctly.
So, this brings me to the end of
the mobility part of this
session.
If there is one thing that we
want you to remember, it's that
whenever a user is walking out
of their home, he should not
have a bad experience and turn
off Wi-Fi.
So, when you are developing your
applications, when you are
testing them and you are
configuring a bad Wi-Fi network
and you are walking out of the
home while testing it, don't
expect your applications to
become slow, to fail, or your
flows to basically-- to take
forever.
It should, in principle, just
work.
If it doesn't, make sure that
those flows that have been
failing are using the high-level
APIs.
These APIs are getting the full
benefit from Wi-Fi Assist from
all the improvements that we did
in iOS 13.
If you are doing active
interface management, come talk
to us in the lab or send us an
Apple Bug Report.
Tell us about your use case and
we will work together with you
to find an alternative so that
you can avoid doing this active
interface management and get the
full benefits from Wi-Fi assist.
And, finally, if you have flows
that are still getting stuck on
Wi-Fi and that are hard to
recover, try to see if you can
start using a multipath service
type, get your servers ready,
and get the same benefits as
Apple Music, Maps, and Siri.
So, this is the end of this part
of the session.
We have seen that with the Low
Data Mode users can now turn on
marked networks so that they
reduce the data usage on those
networks.
We have exposed a new API so
that your applications can
benefit from it as well.
If you are building a publisher
subscriber style application,
Combine in URLSession allows you
to build you a very elegant app,
the same way as Guoye has shown
you today.
Finally, WebSockets.
It has been the most requested
feature is making it into iOS 13
and so you can easily build
two-way communications into your
applications.
And, in iOS 13, we did a huge
push to improve the mobility for
your applications so you can get
the same benefits by using the
high-level APIs.
So, later this afternoon, there
is a part two of the Advances in
Networking session.
You will be seeing more exciting
new APIs that will benefit your
applications.
Tomorrow, for those among you
that are developing on macOS,
there are new APIs for network
extensions, and we also have the
lab at 9:00 a.m. starting at
9:00 a.m. tomorrow and you can
come there to ask us questions
and we will be very happy to
help you out.
So, this is the end.
I hope you had fun and you
enjoyed the session.
Thank you very much.
[ Applause ]