Video hosted by Apple at devstreaming-cdn.apple.com

Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2018-714
$eventId
ID of event: wwdc2018
$eventContentId
ID of session without event part: 714
$eventShortId
Shortened ID of event: wwdc18
$year
Year of session: 2018
$extension
Extension of original filename: mp4
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2018] [Session 714] Optimizing ...

WWDC18 • Session 714

Optimizing Your App for Today’s Internet

Frameworks • iOS, macOS, tvOS, watchOS • 38:10

Learn what Apple has been doing to help your app get the most out of the network with the least effort. Let Apple’s networking APIs do the heavy lifting for you. Learn best practices for getting top networking performance from your app using the URLSession APIs.

Speakers: Stuart Cheshire, Jiten Mehta

Unlisted on Apple Developer site

Transcript

This transcript has potential transcription errors. We are working on an improved version.

Good morning, ladies and gentlemen. How many of you are here for the first time at WWDC? That's fantastic. It's great to see so many new faces every year. My name is Stuart Cheshire. And we're going to be talking about networking. I'm going to start by covering some topics that affect the performance of your app. There's hardly an app that exists today that doesn't make use of networking. And getting the best performance out of the network is really important.

We're going to cover some technologies here that help you get the best performance. We're also going to cover some tips and tricks and guidance about how to make the best use of Apple's APIs, a little bit of news about new technologies on the horizon, and then my colleague Jiten will go into detail on URLSession.

Let's start off with a state of the Internet update. Earlier this year, we hit a total of 4 billion people using the Internet. That's more than half of the world's population. And we're used to Internet usage doubling and doubling. Clearly when we passed halfway, the number of people on the Internet can't keep doubling, so that growth is slowing down but that doesn't mean Internet growth is slowing down.

There's a lot of growth in machine-to-machine communications, Internet of Things, Smart Homes. There's still a lot of growth in places like India and China. And there is a lot of people who have never owned a desktop computer who may never own a desktop computer. Their primary computing and communication device is their Smartphone.

And a lot of those Smartphones are still using 2G networks. I'm sure most of us in this room are fortunate enough to live in places and work in places where we build our apps where we have fast LTE networks. And that can be a handicap because if you build your app so it works well in LTE, it may perform very, very badly on 2G. One of your competitors, somewhere else in the world, who builds an app that works great on 2G is going to be fantastic on LTE. So we have a tool to help everybody mimic some of the properties of these slower networks and that's Network Link Conditioner.

You should build your app running Network Link Conditioner right from the start. Don't think you can add in performance at the end because it's too late. Always, always, always run and test your app using Network Link Conditioner and that way if you make a programming mistake that has horrible performance implications, you see it right away and you can fix it right away.

Use tools like Wireshark and tcptrace to understand the network performance of your app. It's a lot like using Instruments to look at memory and CPU usage. If you haven't seen tcptrace, it is a wonderful tool that produces graphs like this that let you see at a glance what's going on, on the network. If you want to learn more about that, check out the video from three years ago.

IPv6 usage continues to grow. Why is that important? It's important because IPv6 is shown to have better performance than IPv4. And if you care about performance, you want to make sure not just your app but the service your app is talking to support Native IPv6. Some places in the world are doing better in this respect than others. In the US, we're now up to 87% of mobile carriers offering IPv6. Other places like India are doing pretty well too. And let's focus a bit more on India.

Here is some data that the networking team at Apple gathered earlier this year about net TCP connection setup time and ongoing round-trip delay on cellular networks in India. The blue line is IPv6. And if we, for example, look at the 75th percentile, we can say 75% of TCP connections over v6 are set up in less than 150 milliseconds. The comparable number for IPv4 is worse than 325. It's more than twice as slow. So if you want fast, responsive applications for your users, get on IPv6 if you're not already.

Another technology that improves performance by reducing packet loss and retransmission is Explicit Congestion Notification. We've had this enabled by default in macOS and iOS for some years now so you don't need to do anything on your application to take advantage of this. Do make sure your service supports ECN. In a survey we did of the Alexa top million websites, we found last month we're now up to 77% of the Alexa top million services supporting ECN, which is a big improvement compared to a few years ago.

Another technology that helps improve performance and resilience of your connections is Multipath TCP. Quite often, the user may make a connection in their office on Wi-Fi and then they walk outside and they lose the Wi-Fi signal. Now with traditional TCP, the connection is broken. You have to reconnect. You have to start again. Multipath TCP makes its packet routing decisions on a per packet basis not per connection, so it can switch that connection live to a different interface.

We talked last year about how to enable this in your application. And, of course, check with your server operators to make sure your servers are supporting Multipath too. We recently did a survey of the mobile carriers that Apple works with around the world and right now 78% of their networks work with Multipath TCP. Only 22% of carriers are still blocking Multipath connections.

TCP Fast Open is a technology that lets you avoid the normal round-trip delay of the TCP connection set up. TCP Fast Open lets you put your initial data in with the TCP connection set up packets. You can check out more details of that from our video from three years ago. And check with your server operators to make sure that your servers support TCP Fast Open.

Now moving on to some new news. There is a technology that many of you will have heard of called Quick. Quick is a new transport protocol, the first serious candidate in 30 years for a successor to replace TCP. It started off as an experiment by some engineers at Google. That experiment proved successful. It has now been adopted as an IETF work in group item for standardization.

Apple engineers are participating in that. In fact, we have engineers right now at the Quick meeting taking place in Sweden. This is not yet ready for prime time. The standard is not finished, but Apple is working on it. As soon as it is ready, you can expect to see Apple API supporting that.

Continuing in the theme of performance, we observed some behavior that's very common. Lots and lots of websites and Internet services use pretty short lifetimes on their DNS records. And they do this because if a data center goes down, they want to be able to update the DNS and very rapidly direct traffic to a different data center.

The problem with this approach is you're paying a performance cost for something that almost never happens. Data centers very rarely go down. And what this means is every time a DNS address record is expired, your client has to spend another round-trip delay waiting for the response from the DNS server, which is the same as what it knew already last time.

So thinking about this, we realized and optimization we could do. If you pass the flag to opt into this new behavior, then when you do a DNS query, if we have a stale, expired answer in the cache, we will give that to you immediately while in parallel, at the same time, doing the normal DNS query we would have done anyway.

If the answer comes back the same, as we predict it will almost always, everything is fine, you just saved a round-trip time and got your connection started faster. If the answer comes back as a different address, we will then give another asynchronous notification to your client that there's a new address available which it should also try. And to make use of this, you have to use it in conjunction with Happy Eyeballs algorithm. That means your racing multiple connections in parallel.

You're trying IPv4, IPv6, multiple addresses, multiple interfaces. If that sounds like a lot of work and it's hard to get right, you're absolutely correct. It is a lot of work. Stay after the break and we will tell you about some new APIs that let you take advantage of this without doing all the hard work yourself.

Now moving on to some guidance. We have seen a common pattern that many developers use SCNetworkReachability as a preflight check. They want to predict the future. They want to know whether the next network operation they do will succeed or fail. And, unfortunately, predicting the future is always a hard thing to do. You may have connectivity now but two seconds from now the user has walked out of the building and you've lost the Wi-Fi signal. So there is no way to guarantee whether a future operation will succeed.

And we see this pattern where they check. The preflight says yes. They try it. They fail. They go back. They check again. This also is a lot of work, a lot of [inaudible], a lot of difficult things to get right including networks with proxies. We can handle that for you.

The better way to do this is just make a connection using the waitsForConnectivity option. You can learn more about that watching last year's video. What this means is if you want a connection, you tell the system I want a connection. Now if you can, later if not. If the device is in airplane mode, then when it's out of airplane mode your connection will succeed. That is much easier than building the retry loop yourself.

There is one case we've seen with developers which does make sense which is if you're going to have the user answer a lot of information in a form, you may not want to waste the user's time if you have good reason to believe that may later fail. If that is the use case you care about, stay after the break because we have a new way to do that that's much better.

Security remains important, as always. After ten years of using TLS 1.2, the Internet is now ready to move to its successor, TLS 1.3. It has a number of improved security features. It has reduced connection setup time, similar to TCP Fast Open. That standard is now final. The final draft was approved for publication by the Internet Engineering Steering Group earlier this year. We are waiting for the actual published document to come out of the RFC Editor. And when that does, we'll be turning on TLS 1.3 by default. Right now in your seed, it's not turned on by default.

You can use the instructions here on iOS or macOS to enable TLS 13 in your applications. And we encourage you to do this right away because later this year when TLS 1.3 is turned on by default, you don't risk problems with your service not being compatible. So test them right now to make sure everything will go smoothly when the switchover happens later this year.

Another element of security that's new is certificate transparency. You've probably heard cases where certificate authorities, either through malice or incompetence, issue rogue certificates to entities that they should not. The solution to this is something called certificate transparency logs. Every legitimate certificate authority now issues a public statement of every certificate it issues. And those are recorded in public logs for anybody to inspect. And this means that if a rogue certificate authority issues a bogus certificate, if it publishes it, they'll immediately get caught. And if they don't publish it, they'll be caught by the client.

This is the setup you're probably familiar with. The new entity here is the log. When a certificate authority issues a certificate to a server, it also records that with the log and the log gives the server a signed affidavit that its certificate has been publicly recorded. And then when the client connects, the server can give all that information to the client and the client can verify that not only is this a signed certificate, it is a publicly logged signed certificate. Now suppose we have a rogue certificate authority that doesn't publicly expose the rogue certificates it's issuing. The client will reject that because it doesn't have the affidavit to attest to it being recorded in a public log. Starting later this year, we will be enforcing this.

All newly issued TLS certificates must include the verification that they are publicly logged. And if they're not, then the client will reject it. Your apps don't need to make any changes, but if you have tailored certificates for your servers, make sure that your certificate authority is recording them in the public certificate transparency logs.

Now we have a bit of news for hardware developers. The Bonjour Conformance Test is a tool that lets you verify that your hardware devices implement Bonjour correctly. You need to run this test if you want to use the Bonjour trademark name and logo on your packaging. You need to run this test if you want to bundle the Bonjour for Windows installer with a Windows application. And if you want to use the AirPrint, AirPlay, CarPlay, HomeKit logos on your packaging, passing the Bonjour Conformance Test is a part of the logo licensing process because reliable Bonjour is an essential part of those products.

But more importantly, the value of running the Bonjour Conformance Test is it helps you improve the quality of your products and that makes them more reliable which makes your customers happy which makes your customers not return the product to the store because they can't make it work. And that's what your customers want.

That's what you want. And that's what we want. We want happy customers having a wonderful time with products that work reliably. Now I want to cover API choices. Thirty years ago we had BSD Sockets. And it was a great API 30 years ago. But 30 years ago we didn't have mobile computers in our pockets. We didn't have wireless networking. We didn't have IPv6.

We didn't have many computers with more than one network interface. If you had an Ethernet port on your computer, that was a fancy computer. Now 4 billion people around the world have a multi-homed IPv6 wireless battery-powered computing device that does power management and goes to sleep to save energy. The world has become a lot more complicated.

Many of you may be using third-party libraries which are built on that Sockets foundation. Many more of you may be using URLSession. And you may have assumed that URLSession is also just a wrap around Sockets. Well, not quite. URLSession is actually built using Apple's user space networking code network framework.

And starting now, in iOS 12, we are exposing that same API that URLSession uses so that your apps can directly use that for making TCP connections and other appropriate use cases. If you're doing things with URLs and HTTP GETs, URLSession is still your API of choice. But for the things URLSession doesn't cover, we now expose network framework so your apps can use that directly.

And if you're the developer of one of these third-party libraries, which are very popular that are built on BSD Sockets, we encourage you to look at the network framework APIs. Move your library over to these improved high-performance APIs, and give us feedback about how that goes for you.

So to summarize, we really strongly recommend here and now in 2018 that you avoid using BSD Sockets. Avoid using libraries that are nothing but wrappers around BSD Sockets. And if you are one of the authors of those libraries using these older APIs, look at switching over. Come and meet us in the labs this afternoon and tomorrow and give us your feedback about what it takes to move your libraries to new APIs. And with that, I would like to invite my colleagues Jiten to come up on stage and give you more details about URLSession.

[ Applause ]

Thank you, Stuart. Good morning everyone. My name is Jiten Mehta. And I'm an engineer on the CF network team. Today I'll be talking to you about some networking best practices for your apps. Networking is an essential part of every application. Each year, you guys do a great job of adding awesome features to your apps.

And today I'll be talking to you about some simple networking details, details that can help make your apps successful. Our agenda for today is going to cover four categories: reducing latency, maximizing throughput, increasing responsiveness, and making better use of system resources. Before that, let's quickly review URLSession, the API you've been using.

URLSession is the recommended high-level networking API available on all Apple platforms. URLSession has first-class support for HTTP/2 and HTTP/1.1. If your app does not use HTTP, we have support for URLSessionStreamTask, an API that allows you to make secure TCP connections to a server over which you can build your arbitrary protocol.

That's URLSession. Let's move on to our first agenda item: reducing latency. Let's suppose you and your friends go to a restaurant where the waiter walks up to you and you say, "Can I get a glass of water please?" The waiter say, "Sure," walks away, fetches you a glass of water.

Your friend then says, "Can I get a glass of water too?" The waiter says, "Sure," walks away, and fetches your friend a glass of water. Wouldn't it be faster if the waiter took everyone's order at the same time and reduce the number of round trips? The idea to reduce latency is simple. To reduce the number of back and forths when you fetch a resource. Let's see how your apps can do this.

First, let's look at some issues with HTTP/1.1. Your app wants to fetch a resource, you can create a URLSession task and call resume. URLSession will create a new connection for you, which involves DNS, TCP and TLS. Once the connection to the server is established, we will send out your request. We will then wait to get a response from the server. This is the network idle time when your app is not doing any kind of networking, waiting to get a response from the server.

Once we get a response, we will call your completion block or message your delegate indicating that the load has finished. Let's suppose in the middle of this load your app wants to fetch another resource from the same server. You can create another URLSession task called resume and URLSession will create a new connection to fetch this resource since it does not have an idle connection in its connection pool.

If your app wants to fetch yet another resource from the same server, you can create another URLSession task and call resume and we will create another connection to fetch the resource. In this example, I've shown you that we've created three different connections to fetch these resources from the same server. If you notice, we've spent a lot of time opening new connections. Let's see how this would look like if you used a single connection instead.

This is a single connection case. We saved a lot of time by not opening new connections, but there is another problem here. The request number two which is the green request has to wait until response number one is fully received. The same problem applies to request number three which is the orange request which has to wait until response number two is fully received. This problem is known as HTTP head-of-line blocking.

Consider moving to HTTP/2. HTTP/2 uses a single connection, and it also solves the HTTP head-of-line blocking problem. HTTP/2 multiplexes multiple streams over a single connection allowing you to receive parallel responses in an [inaudible] fashion. Let's analyze this example a little more to see how HTTP/2 performs better than HTTP/1.1.

Pay attention to the times when your app wants to fetch a resource and the time when the request is sent out. In the HTTP/1.1 case, there is a significant delay between the time when your app desires a resource and the time the request is sent out. HTTP/2 can significantly reduce this delay and allows us to send the request almost immediately when the app desires the resource.

Also pay attention to these gray boxes. If you recall, this is the network idle time when your app is not doing any networking, waiting to get a response from the server. HTTP/2 can significantly reduce this network idle time allowing you to better utilize the bandwidth and load the resources much faster.

We just discussed many benefits of using HTTP/2 over HTTP/1.1, but let's quickly summarize them. HTTP/2 solves the head-of-line blocking problem at the HTTP layer. And it also allows you to better utilize the bandwidth. If your apps use URLSession, you don't need to make any client-side changes. Simply enable HTTP/2 on your servers and you will see these benefits. By adopting HTTP/2, you can also get some server-side savings because devices running your apps will now make fewer connections to the servers. This year, we have something new in URLSession that is going to add to the advantages of HTTP/2.

Introducing HTTP/2 Connection Coalescing for URLSession. HTTP/2 Connection Coalescing is going to increase connection to use even more. Since your apps are not going to be opening new connections, they will become more responsive to your users. Starting with the [inaudible], HTTP/2 Connection Coalescing is going to be automatically done on for all your apps using URLSession. Now let's see how Connection Coalescing decides to reuse connections.

Let's suppose you have an app and that app wants to fetch a resource from menu.example.com. We open a connection with the server, and the server presents us with the certificate. If your app wants to fetch another resource from delivery.example.com, we open another connection and the server presents us with another certificate.

This is the old behavior where URLSession would create two connections to fetch these resources from the given host names. But if you look closely, the first certificate presented to us covers all the subdomains under example.com which means delivery.example.com is covered by this first certificate. Also notice that delivery.example.com, it results to the same IP address as the first connection.

At this point, it's safe for us to assume we're talking to the same endpoint and reuse the connection instead of opening a new one when we want to fetch the second resource. This saves us time by not opening a new connection and makes the load much faster. HTTP/2 [inaudible] HTTP/2 Connection Coalescing new in iOS 12 and macOS Mojave. Now let's see how using fewer URLSession objects can help reduce latency.

All the benefits of connections we use that we just discussed in the previous slides are applicable only if you use the same URLSession object to create your tasks. It's also important to know that every URLSession object has a connection pool and when you create multiple of these URLSession objects, you don't get any benefit of connection to use. It's also important to note that the URLSession objects are fairly expensive to create and have a non-trivial memory footprint. As we have in the past, we continue to advise you to use fewer URLSession objects.

Let's move on to our next topic for the day: maximizing throughput. Coming back to our restaurant example. The waiter checks up on you and you say, "Can I get an order of grilled chicken tossed in creamy tomato onion gravy made with a lot of butter?" Now that's a mouthful. Wouldn't it be easier if you just said, "Can I get butter chicken?" The idea to maximize throughput is the same where you reduce the number of bytes that you transmit when you want to fetch a resource. Let's see how your apps can do this.

Let's look at a couple of ways to reduce the request size. Pay attention to HTTP cookies. They are not free and have a non-trivial cost in storing and looking them up. Cookies are attached to all the requests that match the domain and path attribute. And it can quickly increase your request size.

Please use the domain and path attribute wisely to make sure cookies required by the servers are attached to your requests. Use of smaller cookies when possible, and delete these cookies when you no longer need them. Try to save some state on the server so you can reduce the number of client-side cookies. Also consider moving to HTTP/2 to get benefits of header compression. Let's talk a little more about compression.

HTTP compression, also known as content and coding, is simply compressing the data that is shuttled between the client and the server. This allows us to better utilize the bandwidth. The algorithms that URLSession supports and recommends are Gzip and Brotli. Gzip is widely supported and is relatively fast. Brotli support was introduced last year in iOS 11 and macOS High Sierra. Brotli is optimized for structured text and HTML. And it has the best compression ratio on short data. Please enable compression on your servers if you haven't done so already.

Let's move on to our next topic for the day: increasing responsiveness. Coming back to our restaurant example. Here you are here in San Jose for WWDC, and you decide to meet up with some old friends. You and your friends are sitting at the restaurant table. Your drinks are here, but you would like some more time to catch up with your friends before the food comes out.

You can simply tell the waiter, "Can you please bring out our food after some time? We are in no rush." The same concept can be applied to responsiveness where you mark your tasks with priority depending on the other tasks that you're doing. Let's see how your apps can benefit from this.

You might be familiar with these five QoS classes associated with dispatch queues and NSOperation objects. Data [inaudible] the CPU scheduling policy. URLSession is QoS-aware which means it will capture the QoS off the queue on which you call task.resume. And all the messages that it sends to your delegates will respect this QoS. Let's take an example. If your app wants to fetch some data which is not time critical, consider resuming that task on a queue which has background QoS to make sure this task does not contend for CPU with other higher priority work that your app might be doing.

Network service type is the property on the URLSession configuration object that allows you to classify your network traffic that helps the system prioritize the data leaving the device. This year, we have a new network service type, the responsiveData. ResponsiveData is slightly higher than the default type but should be used judiciously. An example where you might want to use responsiveData is if you have a shopping app and you are on the checkout page. You might want to mark your payment request with the responsiveData to make sure you get a good response from the server.

Traffic marked with the network service type property will maintain this tag across all the hops when on a Cisco Fast Lane network. For more information on this API, please view the WWDC session from the year 2016. Last year, we introduced the URLSession Adaptable Connectivity API waitsForConnectivity. waitsForConnectivity will simply wait instead of failing the load when your task does not have connectivity.

In the past, you've been using STNeworkReachability to do a preflight check before you send out your request. But as Stuart pointed out a few slides ago, there is a race condition where the system might tell you that you have connectivity to a server but by the time you create and send your request, you've lost your chance and you're no longer connected to the server.

We recommend using waitsForConnectivity which will simply send out your request as soon as a connection to the server is available. Optionally, you can implement the taskIsWaitigForConnectivity delegate method which gets called when your task does not have connectivity. This can be helpful to present the user with a different flow or an offline UI for better user experience.

For more information on this API, please view the WWDC session from last year where this API was introduced. Now let's move on to our last topic for the day: making better use of system resources. Coming back to our restaurant example. You like the food at this place so much that you decide to come here for dinner the next day.

The restaurant has a delivery service where you can place your order today and they will deliver the food to your house the next day. This not only saves you the time and effort to go and pick up your food but it also helps the restaurant prioritize their work based on your deadline. Let's see how your apps can make better use of system resources to be more efficient.

Background sessions have upload and download tasks. These tasks use system intelligence to decide when to start and when to stop a download based on various factors like battery, CPU, Wi-Fi, etcetera. If your app wants to fetch a large file, consider using background sessions. These tasks run out of process which means your download will continue even when your app is in a suspended state. For more information on background sessions, please view the WWDC session from the year 2014.

Caching is a great way of reducing latency but it's important to note that caching might result in disk IO. In the real world, we've seen some apps write several gigabytes of data to disk each day which can cause severe flash storage degradation. Please don't cache unique content. Let's take an example. Let's suppose you have an app, a dating app, and you are responsible for the networking code of the app.

This app loads user profiles with high-resolution images. It might be wasteful to cache these high-resolution images because the user might swipe left, move on to the next profile, which means that the images that you just cached are probably not going to be requested again. Please consider making client-side changes by adopting the willChacheResponse delegate method to decide what resources should be cached. If you own the servers, please consider using cache control headers to decide what resources should be cacheable.

Let's quickly go over some of the key points that we discussed today. Number one, order all your food at the same time when you go to a restaurant. I'm just kidding. Move to HTTP/2 today to get wins like header compression, connection coalescing and no head-of-line blocking. Use fewer URLSession objects to reduce latency by reusing connections. This also reduces the memory footprint so it's better use of system resources. Reduce the request size to maximize throughput.

Pay attention to QoS to increase the responsiveness of your apps. And finally use background sessions when you can to make better use of system resources. For more information on this session, please visit this website. Now we'll have a short break. And after the break, we'll introduce you to network framework, a modern alternative to Sockets. I would love to see you all at the networking labs which are going to be held today and tomorrow. Thank you all for being here. And I hope everyone has a great rest of the conference.

[ Applause ]