NSURLSession provides a rich set of methods to support uploading and downloading content via HTTP and newer HTTP-based protocols, authentication with web servers, local caching of resources, and transfers that occur in the background while your app isn't running — simplifying the complexities of modern day networking. Learn about new enhancements in HTTP/2, introspection capabilities with networking metrics, and security and privacy enhancements for you and your customers.
Thanks for coming and being here today. This is NSURLSession: New Features and Best Practices. My name is Jeff Jenkins. I'm a Software Engineer in the Internet Technologies Department. I think we have some great information that we'd like to share with you today within the NSURLSession. So why don't we just get started and jump right into an agenda and show you some of the things we're going to look at.
The NSURLSession API provides really a rich set of classes and methods that simplify the complexities of modern day networking.
Today I'm going to discuss with you how the NSURLSession API has evolved. And I'm going to share with you some new features and enhancements that we've made to this API. And we're going to have a demonstration that'll show you some of these features and actions in a real application.
I'll spend some time discussing security.
It's important for your users that their data and privacy be safeguarded. If it's important to your users, we know it's important to you and to your applications.
So I'm going to share with you some of the enhancements we've made to technologies in NSURLSession with regards to use security.
Now, during the entirety of this session I'm going to be sharing with you some best practices and tips so that you can make your apps even more incredible. Now, let's start by looking at the highest level APIs available to applications across all of Apple's platforms.
For many years, Foundation provided the NSURLConnection API.
Now, NSURLConnection was a great network abstraction. It enabled a lot of great fundamental networking functionality for you developers in your apps.
But you know, we asked ourselves: What could we do better? And what is it that you developers are asking us to do? And we sat down and we talked about this. And we found that a lot of the answers had to do with configuring networking -- that configuring network in an app can be difficult, especially if you need to do different types of networking all from the same application.
So, we took action.
We officially deprecated the NSURLConnection API in 2015. And this really means we're not adding new features to that API. And it's really receiving very minimal maintenance.
But prior to the official deprecation at WWDC 2013, we introduced NSURLSession as the replacement API for Foundation layer networking.
Now, we encouraged you then, we continue to encourage you now to move your networking code to the NSURLSession API in your applications today and apps that you're going to be developing in the future. Now, since NSURLSession is the best high-level API for network programming on Apple's platforms, let's review some of the basics of NSURLSession.
Now, if you want a real in-depth detail, you know, inside into NSURLSession, I recommend that you go back to WWDC of 2015, and especially 2014, and take a look at the videos and slides with the sessions regarding NSURLSession.
So, I've been praising NSURLSession API and recommending that your apps use this API.
So what really makes NSURLSession worthy of all this praise? Well, some of the benefits, we continue our great support for the HTTP/1.1 protocol. We also have support for the SPDY protocol. Now, we don't recommend that you build new Web services based on SPDY, but really that you look ahead to HTTP/2 and that protocol. We obviously have support for HTTP/2 in NSURLSession.
Our support is based on RFC 7540.
And we're adding new features of this protocol all the time. The app transport security, or ATS. This is a great feature built into NSURLSession.
It's a security feature that improves the privacy and data integrity of connections between apps and Web processes.
One of the most important aspects of ATS is that your apps must use the HTTPS protocol.
Now, at the same time it allows you, the developer, to implement best practices when making connections to remote servers over the HTTPS.
The HTTP Strict Transport Security, or HSTS, another great feature built into NSURLSession.
This feature protects your users' data and privacy. Now, it can be configured via an HTTP header delivered from your Web service.
Or it can be configured using a preloaded list embedded in the system along with your application.
Once configured, all the data that is transmitted, sent, received over a secure connection.
The great thing about HSTS is it does not require any code changes in your applications.
All the great built-in support subsystems built in for the handling of caches, cookies, proxies, authentication challenges, all these things are built into NSURLSession. And last but not least, configuration. As I said, we talked a lot about how can we make networking or configuring networking easier in your applications? We feel that we need to have better control over networking. So, we looked at configuration hard. So what we did to achieve fine-grade and control over your networking in NSURLSession, we created this new class called NSURLSessionConfiguration.
Now, this class contains many properties that you can configure.
You can create configurations based on different networking needs within your application.
Now, here's a sample of just some of the properties that you can set with NSURLSessionConfiguration.
The Transport Layer Security or TLS version.
You can control the minimum and maximum versions of TLS that your app wants to support.
We default the minimum TLS to 1.0 and the maximum TLS to 1.2.
You can control the use of cellular by your application. Do you want to let your app use the cellular network or no? Maybe you want Wi-Fi only networking. This is the place to do that.
You can specify your network service type.
Some examples of this might be just default, which is the standard internet data traffic. Your app might have VoiP control or video data or voice data. All sorts of different types of data. And really, this is just a hint to lower networking layers of what type of data to expect so that it can create a quality of service that's best for your app on the device that it's running on.
Similar for cache. Maybe you want an ephemeral cache, persistent cache that's used between app launches.
This is a place to set that policy.
You can also specify storage objects. Well, maybe you want to share caches between different types of networking or cookies between different types of networking, all within your same app.
This is a place that you can set the storage object for that configuration.
And time-out specifiers. You can set resource and request time-outs so that your app can handle error conditions in the network.
Now, I expect many of you have some exposure to NSURLSession API. If not, really, go ahead and look back at the WWDC 2014 on NSURLSession. You're going to get a lot more in-depth instruction on how that API works. But I think for us in the remainder of the talk here, it's good for us to take a look at how NSURLSession works from a code perspective.
So very simply, the first thing we do is we're going to create a configuration object. First thing we do. Here we call NSURLSessionConfigurations .defaultSessionConfiguration.
We take all the defaults and we've got a very simple config ready to go. Now, we take that config and we give it to a session. We create a session by calling NSURLSessions emitter that takes a config object. And boom! We've got a very simple session ready to go.
Now that we've got this session, we're ready to do some work. Now, the first thing we're going to do is create an NSURL and specify what's the endpoint that we want to retrieve or request data from? Once we have that, we can create a task. Here we call the dataTask function. And we pass the NSURL object that we created to that dataTask.
We also provide a closure.
Now, this closure is called asynchronously when our task is finished loading and the requested resource is returned to us.
Now always, don't forget, make sure you call task.resume. All of our tasks are created in a suspended state. So you must call task.resume to get that task executed.
All right. So in review, very simple review, NSURLSession is really a three-step process.
First you're going to create your configuration object.
Once you have that config you're going to create your session.
And now you're ready with that session to make it do some work and you're going to create tasks on that session.
Now, I want to suggest to you a best practice here.
Want to make sure that you avoid the one-task to one-session model.
You should really not set up your networking this way.
Really, what you want to do and how you want to design this is have one session service many, many tasks. So of course, you can have multiple sessions.
But avoid the one-session to one-task model. And the reason for that, it really boils down to performance. You're going to get better memory management and OS resource utilization by having few sessions servicing many, many tasks. Now, as I mentioned, the NSURLSession API is evolving.
I'd like to talk about the HTTP/2 protocol and what we've been doing in this protocol within NSURLSession.
Now, the support of the HTTP/2 protocol within the NSURLSession was introduced last year at WWDC. And it's increasing in popularity.
You know, large Internet service providers are supporting HTTP/2.
Now, what makes HTTP/2 such a compelling protocol? Well, let's look at some of its features. One of the great features of HTTP/2 is multiplexing and concurrency support.
Now, this feature allows multiple requests and responses to be in flight concurrently.
Responses can be received out of order and all on the same single TCP connection.
Another great feature of the protocol is header compression.
This reduces the size of the HTTP/2 headers.
It cuts down on network round trips. This is better for bandwidth, and really better for performance for your application.
Another great feature is stream priorities. As a client of HTTP/2 you can indicate the priority at which resources are returned from the server. And this really allows you to look out and tune the networking for your application.
So in essence, HTTP/2 is all about performance. Now, this will result in an improved end user experience as they interact with your apps.
Now, there's one more feature that we have to take a look at of HTTP/2.
Today we're introducing support in NSURLSession for HTTP/2's Server Push feature.
So what is Server Push? Well, when a client makes a request, a server will respond to that request.
But concurrently, it can push additional responses to the client. So if you think about a Web page, it's made up of lots and lots of resources.
Instead of the client having to fetch each individual resource over the network, a server can, in parallel to the original response, push information about additional resources the client is likely to need.
So, Server Push prevents multiple network round trips that HTTP/1.1 and non-Server Push HTTP/2 requires today.
Now, I want to bring to your attention something that the server obviously has to support this protocol, has to support HTTP/2 and it has to be configured to enable the Server Push feature.
Now, Server Push is available now to applications that use NSURLSession. There's no need to opt in. No funny properties that you have to set. It's just there. And it just works.
So this is all more reason we feel that NSURLSession API is the best API for your applications today.
Now, let's take a minute here and look a little bit deeper about how Server Push will benefit your app.
Here I've got a graph that represents an app's use of NSURLSession using the HTTP/1.1 protocol to do some network loading.
Now, as we move down this graph, we're going to accumulate more time. And this will be our total latency for our network loading for our application.
So, first our app is going to resume a task.
And that task is going to request index.html.
We'll assume that everything's working great because networks just do that. And we're going to get our response from the server with the requested data.
Now, in addition to the data for index.html, we note that time has passed.
And that our total latency has accumulated, has increased.
Now, our app is going to resume another task and request style.css from the server. And again, assuming everything's working properly, we're going to get that response.
And now we have style.css and our total latency has now increased by the time required to fetch that resource over the network.
Here the application will make the final task resume and request background.jpg.
We get the response from the server.
And here we've got background.jpg. Our application is now finished with its network loading.
And we look at the graph, we see this is our total latency cost for an HTTP/1.1 load.
Now, let's take a look at our application when it's able to use HTTP/2 and Server Push.
And I'm just going to shift the HTTP/1.1 result graph over to the side just for reference.
So again, our app will resume a task request index.html.
Again, our total latency, we receive our response. And our total latency is pretty much exactly the same as it was for the non-Server Push case.
That's expected. What will happen, however, is that concurrently to our response for the original request, the server will push other resources to NSURLSession.
Now, this comes along with the original request or a response from the original request.
When our application resumes the second task and requests style.css we get an almost immediate response. And this is because Server Push gave us the data before we really knew we needed it.
Now we have style.css and we notice that the accumulated time for style.css is substantially shorter when compared to the HTTP/1.1 needed to load the exact same resource. Now the app makes some final task resume and requests background.jpg. Again, we get a real quick response.
The app has background.jpg. And we add the total time to our accumulated total. At this point the app is done with its network loading.
And we see that the HTTP/2 with Server Push latency is much smaller than that of the HTTP/1 or the non-Server Push load.
The reason for this improved performance is that with Server Push we were able to save the latency cost of network round trips needed when requesting resources.
Now, I'd like to invite Andreas Garkuscha to show us what Server Push looks like in a real application. Andreas.
Thank you, Jeff.
Good morning everyone.
Today I'm going to demonstrate the advantages of using HTTP/2 Server Push when loading resources for your apps.
You are looking at the very common part of an app that many of you may already have implemented or at least have seen while using some of the existing apps.
What you see now could be a social network app for photographers showing the most popular pictures where you can list the most popular picture for a certain photographer.
View the individual pictures.
Go to the next.
Go back. Go back to the top list. Choose another one to view.
And so on.
This app could be a food recipe app or a music streaming app showing the album artwork. So an app which is loading resources from your server. In this case, it is loading and showing some images.
Now notice here for demo purposes there is a switch.
It allows us to choose between the initial URL with Server Push configured and one with no Server Push.
In both cases, first we request the initial document containing the image URLs to load.
Then we request the images.
If the Server Push is off, nothing is getting pushed. So we need to send the requests for every image over the network.
In case of the Server Push, and now I'm going to switch to this mode, as Jeff explained previously, the requesting of the initial document will trigger the push of the resources from the server to your application.
There will be no need to send the request for every image over the network. The data for your data tasks will be delivered out of the Server Push storage directly to your application.
Now, let's compare the performance of the Server Push and non-Server Push loads.
I'm going to switch between the initial URL with Server Push and non-Server Push a couple of times so that you can see when the Server Push is on, this entire collection view is loaded faster.
But how much faster? Well, let me show you that.
Now the demo application is going to provide the results.
It's the bottom of the current view. For the fastest non-Server Push load in red.
And for the fastest Server Push load in green.
Let me do it a couple of times.
Now the same with the artist view.
A couple of times. Server Push off. Server Push on.
You can see here that the Server Push load is at least two times faster.
Pushing the images is about two to three times faster than loading them on the high latency network we are actually using here.
The kind of network your applications can experience when on cellular or slow Wi-Fi.
I prepared a video showing the Server Push load and non-Server Push loads side by side. Well, let me show you that.
So, you can see when the Server Push user is already viewing the pictures, the other guy is just trying to load the artist view. Let's see it one more time.
Once again, the Server Push user on the right is getting through much faster than the non-Server Push user on the left.
Two to three times faster. Just think about it.
Think about the user experience you can provide your customers if you configure the Server Push on your HTTP/2 server.
And you don't even have to change any code in your app. And this is great. Just awesome.
This was the Server Push demo. Thank you very much. Have a great conference.
And now, back to Jeff.
All right, thanks, Andreas.
So let's summarize what we've seen and discussed regarding HTTP/2 and Server Push.
Server Push is supported only in NSURLSession API. So if you're writing new apps or you're on NSURLConnection, we highly recommend that you move to NSURLSession APIs today.
There's no coding that you have to do to take advantage of Server Push. It's not an opt-in. It just works.
And with Server Push your apps will perform in the most optimal manner, giving your users an even better experience with your applications.
Now, another evolution to the NSURLSession API is the addition of Network Statistics.
Now, who out here loves statistics? We have any statistic people? We got some folks that love statistics? All right, I've got a stat for you. Are you ready? Here it is.
All right. Right. Sorry about that. Sorry. We're going to get to some actual useful statistics. And this is, we believe, network statistics are part of that 2% useful values.
So, why is it useful? Have you ever had slow network performance in your applications? Imagine some of you have. And how did you go about figuring out what was wrong? You know, how did you debug that? We believe network statistics is going to be great for you guys to be able to use in the development and debugging environment. You could use this to collect information, possibly out in the field. Maybe you could leverage Test Flight and collect some information about your app running in the field.
Network statistics is about analyzing performance of network loading within your app. It's about finding and fixing networking-related bugs. And it's about giving you better understanding of what your networking is doing inside of your application.
Currently, network statistics is supported in iOS, macOS and on tvOS platforms.
Now, before we get started with the actual statistics values, let me explain to you how you're going to get all these values. And to do this, we need to look at a little bit of code. Here we have a brand new delegate called didFinishCollecting metrics.
This is a delegate method of the NSURLSession Task Delegate class.
Now, when you implement this delegate, it's going to be passed a task for which the metrics were collected and a new class object. This is NSURLSessionTaskMetrics.
The NSURLSessionTaskMetrics class has a property named taskInterval.
This is the interval of time from a task creation to the point in time when all the statistics are collected and are ready to be delivered to your didFinishCollectingMetrics delegate.
Another property I'm showing you here is the redirectCount.
Now, that's fairly straightforward. And as the name indicates, this is the number of times that an HTTP redirection occurred during the task's execution.
The last property of NSURLSessionTaskMetrics called transactionMetrics.
Now, this property is really the core. This is the meat of our network statistics. This is where you get an array of NSURLTaskTransactionMetric objects. Now, let's take a closer look at this particular new class because this is really where some of the great metrics are located.
Now, in order to understand all of these metrics, we're going to chop them up into four categories.
Now, the first two properties of NSURLTaskTransactionMetrics make up the first category. And I'm going to call that Request and Response.
There's two properties here called Request" and "Response.
And they really allow you to analyze what was it that I asked for? And what was the response to that original question? The second category of statistics has to do with protocol and connection.
The property networkProtocolName is just -- it tells you what type of protocol was used during the time at which statistics were collected. And here's a possible list of names you might see here: HTTP/1.1, HTTP/2, or SPDY. Now, this list can and will change over time.
Another property you'll have is isProxyConnection.
This tells you whether the transaction was part, or had a proxy connection involved during the time collections were gathered, or statistics were gathered or collected.
And the isReusedConnection. This property is set to Yes if a persistent connection was used during the fetch of the resource.
Now, the third category of metrics has only one property. And it deals with the information about the resource loading or load info.
The single property is resourceFetchType.
Now, it tells you how a resource was obtained. Now, some of the values you would see here are network load. This indicates that the resource was loaded over a network or, as commonly referred to, an origin load.
You might see local cache, which indicates the resource was fetched from a local cache. It was stored local to your application. No network transaction was really required.
And Server Push. This tells that the resource was found as a result of a Server Push cache hit when you made that request.
The fourth category of transaction has to do with connection establishment and transmission.
Now, the first group of metrics measure time related to the setup of a network connection.
The second group of metrics measure HTTP-related activities of a network load.
These properties are timestamps and are taken at a time when the event actually occurred. So let's take a closer look at these metrics and when they are collected during the execution of a network load.
To do this, we're going to look at a very simple network load. Now note that this is just one model of one type of load. There's all sorts of activities that happen during network loading. You've got cache lookups. You've got cookie lookups. You're dealing with redirections, sometimes authentication challenges. But we're going to keep it real simple just for our purposes here. The NSURLTaskTransactionMetrics class contains properties that contain all these time stamps at various points. So, we begin with fetchStart.
fetchStart is a time when the application begins requesting a resource. A fetch could be satisfied from a local cache, or possibly resource that was origin loaded.
So now we're looking at domainLookup.
So the domainLookupStart property. This is the time just before a name lookup for a resource begins. Now, this is the DNS query.
And this converts a host name to an IP address. So, domainLookupEnd is when that lookup completes. And an IP address is sent back to the caller.
Here we're going to take a look at what metrics we collect for connection establishment.
The connectStart property is the time just before an app begins to start or establish a TCP connection with the remote server.
Now, this value could be nil if the response is found in a local cache.
I'll discuss the connectEnd property in just a moment.
If you're doing HTTPS you're going to need a TLS handshake. So we've got a statistic for that.
The secureConnectionStart metric, the point in time just before the application starts the security handshake to secure the current connection.
And secureConnectionEnd, well, that's when the secure handshake is completed.
It's finished. Now, as I mentioned, connectEnd. Now, that's the time immediately after an app has connected to the remote server, including all the security-related handshakes.
This is the point in which a connection is considered established.
Now that we have an established connection, we can actually do some HTTP. So, we have a few stats that we can collect for you regarding the request and response. So first we have requestStart. And this is the time when the app begins requesting the resource regardless of whether the resource was fetched from a local cache or a remote server.
If an origin load was needed, this is the time at which the first byte of the HTTP header began transmission.
requestEnd is the time when the last byte of the request was written to the network.
This represents a time when the first byte of the response is received from the server.
Now, if this is a cache load, this is when the cache response was received from the cache. And responseEnd was the time immediately after the application receives the last byte of the resource requested.
Now that we've been looking at stats collected during an origin load.
That is, we had to send bytes over a physical network.
It's possible that the networking could have been satisfied from a local cache. And I've mentioned that. So if a local cache contains the response for our request, then there's no need for DNS. So there's no need to make a TCP connection, et cetera.
Those statistics will be set to nil in this cache case. So just be aware of that as you're looking at these statistics.
Now, you might be asking, "Okay, this is really cool. How do I get these stats into my code?" So, to do that, let's look at some code. Now, as I showed you earlier, we start with the delegate. And here I have an implementation of the NSURLSessionTaskDelegate.
This implements the new didFinishCollectingMetrics delegate callback. And the one I'm showing you here doesn't do really anything of any interest at the moment. I totally expect you guys are going to do some really cool stuff here. Maybe you're going to do some logging. A good point to do some debugging and take a look at what's happening with your networking. Again, maybe leverage Test Flight and do some logging so you can collect some information from the field about how the networking is performing in your applications.
That's all up to you.
So, let's keep looking at this example here. Maybe a little bit of housekeeping code. First thing we have to do with our delegate implementation is instantiate an instance of it.
Now, we're going to create a default configuration object. We take all the defaults just by calling defaultSessionConfiguration. Next, we're going to create an instance -- or we're going to create an NSOperatonQueue instance. And we have to do that because we have to have some place, some queue, so that our delegate can actually do some work. And as I mentioned earlier, I recreate an NSURLSession with that configuration object. But here I'm calling NSURLSession's init function that takes additional parameters. And namely, those are the instantiated instant delegate object and the NSOperationQueue object that we created.
So now we can actually do some work. We create a task with our session. And what do we want our task to do? Well, let's just simply load this server's root Web page.
And here I'm using, again, the dataTask method.
And this requires a closure as a parameter.
Now, note with this closure that our didFinishCollectingMetrics delegate callback will be called first and called before that closure is invoked.
So, oh, and also, don't forget your good old task.Resume. Got to make sure you resume that task so that the work can actually execute. Now, this is an overly simply review of how to implement networkStatisticDelegate callback and the code needed to make it work. I'm sure that you guys are going to do much more interesting things and be much more creative with your use of Network Statistics that I've been here.
Now, I hope you like what you've seen with Network Statistics. What do you think of those? All right.
Now you can access information about what's really happening under the hood with your network transactions.
This will help you in your debugging and development to be able to get, you know, fix your apps and make your apps work the best that they can be. And tune, really tune the Web services and the types of apps that you're making so that they will work in the most optimal way. And again, this new feature is available in NSURLSession APIs. It's a great time to be using NSURLSession. Now, in the final section I'm going to discuss a topic that's critical to everybody. And now, as developers, we all care about guarding the privacy of user data.
The NSURLSession API has some great built-in security features. And I want to share with you just a few enhancements we've made to these features.
Transport Layer Security, or TLS, is a protocol that protects data transmitted by endpoints over a network. So sometimes you've heard the term SSL. This was the predecessor to TLS. So, TLS and SSL are sometimes used interchangeably.
TLS makes use of ciphers to achieve this protection. Ciphers scramble data on one side -- scramble data sent over the network. And the other receiving side uses the same cipher to unscramble that data so that it can use and understand it. If somebody were to grab that data in the middle, it's just big glob of blah. They can't use it. So it protects the data.
Now, one of the things that has changed, and I want to make you aware of, is that Apple's platforms no longer support the RC4 cipher.
So for more details, I recommend that you take a look at, review the security sessions that occurred on Tuesday.
Possibly attend one of their labs and ask some questions there for a little bit more detail on that.
So, what this means to your applications, however, is that connections that you used to make successfully could possibly fail suddenly. And this could be due to servers that are supporting only the RC4 cipher.
Now, we have a way that you can kind of test this theory out.
There is a command line tool on macOS called nscurl. And this could be used to test for RC4-only ciphers on servers that your apps communicate with.
Now, here I'm going to use nscurl simply to grab the root document of this server. So if I execute this command -- whoa! I see that, hey, the HTTP Load failed. Hmm. That's funny. I used to be able to connect that server. Let's find out if our theory about RC4 is true or not.
Here I've added a flag to nscurl called enable-rc4. And that means that I am going to force the use of RC4 on the client's side of the connection. So I run this.
Wow! This worked all of a sudden. So this proves to me that this server is only supporting RC4. I'm going to have to go have a talk with that server admin and figure out what could we do to get some secure ciphers onto that server so we can protect our user data within our application. So I hope you get the point here.
The RC4 cipher is no longer supported on our platforms. And this could affect your applications. So just be aware of that.
App Transport Security, or ATS. This is a great security feature. It allows you to set the policy, security policies for your app. You specify keys that grant entitlements.
And these become your policies for your applications. Now, we've added two new keys that I want to share with you.
The first one is NSAllowsArbitraryLoadsIn WebContent. So, if you have a WK Web view and have scoped only to that class, if you have that class in your application and you have this key present, that object will be allowed to make any sort of network loads that you have specified. It'll ignore whatever other policy you have in your app. But again, that's contained only to that one object. Any loads outside of that object will conform to your security policy.
The other value we have is NSRequiresCertificate Transparency. Now, this requires that certs that your app receives must support these certificate transparency feature.
Certificate Transparency is a feature of signedCerts. This allows you to use a provided certificate with greater assurance that the certificate is legitimate. And that you can trust the endpoint that gave you that certificate.
For more details on that particular subject, again, recommend that you take a look at these security sessions or attend one of their labs to get more details on Certificate Transparency.
Everything that I've discussed relates to the NSURLSession API.
We've seen this API evolve with new features that have been added. And we're really excited to see what you guys are going to do with your applications as you take advantage of some of these great new features.
I introduced support for the HTTP/2 Server Push feature.
This means that your apps are going to be able to perform even better.
I introduced Network Statistics.
Now you can introspect your application network behavior like never before.
This will result in an improved end user experience for your applications.
I shared with you some security-related features in ATS and the deprecation of the RC4 cipher.
Your apps can now implement best security practices and keep your user data safe and protected. Now, we hope that you will use NSURLSession in your applications for not just these reasons, but for all the great features that are inside of NSURLSession API.
If you want more information, this is the URL for this session. So you can go back and review some of the things we discussed today.
Some related sessions we think that you will be interested in on this subject I've got listed up here for you.
Well, that's it. Thank you. Hope you have a great rest of the WWDC.
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.