What‘s the best practices of NSURLSession? - ios

Subscribe my app first:
Most scenes use AFNetworking, a small part of scenes use NSURLSession.sharedSession or create a new NSURLSession.
Using one URLProtocol instance to handle almost all requests, and at the end of -startLoading function, using only one NSURLSession to resume all tasks.
My question is:
I know URLSession instance will cause memory growth and it persists for about 10 minutes, so what is the maximum limit for an app to hold URLSession instances?
What‘s the best practices of NSURLSession?Is it recommended to use only one URLSession instance for the entire app? Or a fixed domain uses a fixed NSURLSession( A-domain using A-session, B-domain using B-session)?
Should I create several URLProtocol instances to handle different domain requests
Thanks!

You want to use as few URLSessions as necessary. Usually, this is only one and it's also unlikely you invalidate this session and create a new one during the lifetime of the app.
The reason for having as few as possible, is that URLSession is specifically designed to handle more than one network request - both executing them in parallel or sequential - and can optimise all requests executed in this session in order to use less memory, less power and achieve faster execution times.
On the other hand, there are far less options to optimise requests executed in different URLSessions. Especially performance gains from using HTTP/2 cannot be achieved for requests running in different URLSessions.
However, there may be requirements or situations where you create more than one. For example, you utilise a third party image loading library which creates its own URLSession. Or you need distinct URLSession configurations, like cellular usage, cooky policy or cache policy, etc., which cannot be shared.
Or for example, you want to tie a certain URLSession and it's URLCache and Credential cache to a certain authenticated user. When you have some "sign-out" feature in your app, you can invalidate the session, clear the credential storage and the URLCache. At the same time you have another URLSession for your "public" API, and another URLSession for your image loading, which are not affected by a "sign-out" - and where cached responses should be kept.
It's a gross no-no to create a URLSession for every request, then let this hang around when the request completes and create another URLSession with the next request. You can't do it worse than this.
See also
Apple Developer Forum
WWDC: NSURLSession: New Features and Best Practices
WWDC: Networking with NSURLSession

Related

Are URLSession objects resource intensive?

Would it be resource intensive to create a new URLSession for every single web request?
Some background:
I'm working on a library for making web requests. I'm trying to add a feature that allows downloading the result to a file that would also report its progress. For that, I'm going to have to become the session's delegate.
This wouldn't be a big deal except the public interface allows customizing the URLSession used for the requests. I don't want to override any customization the developer wants to do with its own delegate.
Right now, I'm thinking that the way to do this would be to secretly make a copy of the session they think is being used (yes I'm going to do more than copy the object itself) and then my internal delegate would call out to the original public session's methods. There could still be confusion/problems if they try to manipulate the session during the request, but that seems like a much smaller edge case.
My only concern right now is this might be very resource intensive if many requests are being made. Does anyone have a sense for that?
Yes, they are intensive. Here is a quote from Apple Staff on the developer forums.
This is a common anti-pattern, one that we specifically
warned against at at WWDC this year. Creating a session per request
is inefficient both on the CPU and, more importantly, on the network.
Specifically, it prevents connection reuse, which can radically slow
down back-to-back requests. This is especially bad for HTTP/2. We
encourage folks to group all similar tasks in a single session, using
multiple sessions only if you have different sets of tasks with
different requirements (like interactive tasks versus background
download tasks). That means that many simple apps can get away with
using a single statically-allocated session.

NSURLSession and multithreading for downloading multiple files

I have an app on iOS which wants to download data from my server. The data is nothing but simple text files. I would like to download these files in parallel.
I am getting confused if I should be creating multiple instances of nsurlsession or multiple tasks(NSURLSessionTasks) under one nsurlsession.
I do know nsurlsession APIs are thread safe. And my fundamental confusion here is about following thing:
My NsurlsessionConfiguration is going to be same for the entire time. So, ideally I can use the same instance of NSURLSession for every file.
But does it make sense to create multiple instance of NSURLSessionTasks in parallel?
Or better approach is to take make a new NSURLSession for achieving the parallelism.
I am confused to understand if every NSURLSessionTask in the same NSURLSession creates a new end point or it is serialized.
One session with many tasks -- one per request -- will work safely concurrently. Whether the client makes requests across many servers or to a single one makes no difference so long as the concurrency is less than HTTPMaximumConnectionsPerHost which is part of the session config.
The tasks run in parallel. (It's actually pretty hard to find a place in the docs where those words appear explicitly, but there's pretty good implication here).
Notice that session configs have a delegateQueue. The rationale is -- because the sessions tasks are run concurrently -- the app level needs to serialize handling of the results, lest two parallel tasks step on each other via the delegate.

URLSession across different REST endpoints for the same server

I have an app that makes a whole bunch of different REST calls to the same server from a bunch of different view controllers. What’s best practice with regard to URLSession: Share the same URLSession object? Or just the URLSessionConfiguration object? Or doesn’t matter about either?
For example, when making request to an endpoint, should I
Instantiate a brand new URLSession each request with with the shared URLSessionConfiguration?
Instantiate a single URLSession once for the current active app instance, and reuse it across all requests?
It is not best practice to create multiple URLSessions. Apple recommends creating only one if possible:
WWDC2017 Advances in Networking, Part 2
"We have seen developers that take their old NSURLConnection code and convert it to the new URLSession code by mechanically making a URLSession for every old NSURLConnection they used to have. This is very inefficient and wasteful. For almost all of your apps what you want to have is just one URLSession, which can then have as many tasks as you want. The only time you would want more than one URLSession is when you have groups of different operations that have radically different requirements. And in that case you might create two different configuration objects and create two different URLSessions using those two configuration objects."
Though the Apple developer presenting this session was answering a slightly different question, clearly the answer he gives is good for your question too.
A long-lived shared URLSession object only makes sense if you need to use methods on that class that affect multiple tasks at the same time. For example if you need to call getTasksWithCompletionHandler(_:) or finishTasksAndInvalidate(), the session object needs to exist for long enough to cover all of the tasks you want those methods to affect.
It might also make sense if creating them on the fly would result in having several identical instances at the same time.
Otherwise, create a URLSession when you need it and then let it get deallocated when you don't.
In either case I wouldn't keep a shared URLSessionConfiguration object in memory at all times. Set up a factory method that can create one, and call that whenever you need a URLSession.

Async NSURLConnection triggering other Async NSURLConnection: what is the best way of doing this?

this is an open question aiming at understanding what is the best practice or the most common solution for a problem that I think might be common.
Let's say I have a list of URLs to download; the list is itself hosted on a server, so I start a NSURLConnection that downloads it. The code in connectionDidFinishLoading will use the list of URLs to instantiate one new NSURLConnection, asynchronously, per each URL; this in turn will trigger even more NSURLConnections, and so on - until there are no more URLs. See it as a tree of connections.
What is the best way to detect when all connections have finished?
I'm aiming the question to iOS7, but comments about other versions are welcome.
A couple of thoughts:
In terms of triggering the subsequent downloads after you retrieve the list from the server, just put the logic to perform those subsequent downloads inside the completion handler block (or completion delegate method) of the first request.
In terms of downloading a bunch of files, if targeting iOS 7 and later, you might consider using NSURLSession instead of NSURLConnection.
First, the downloading of files with a nice modest memory footprint is enabled by initiating "download" tasks (rather than "data" tasks).
Second, you can do the downloads using a background NSURLSessionConfiguration, which will let the downloads continue even if the user leaves the app. See the Downloading Content in the Background section of the App Programming Guide for iOS. There are a lot of i's that need dotting and t's that need crossing if you do this, but it's a great feature to consider implementing.
See WWDC 2013 What's New in Foundation Networking for an introduction to NSURLSession. Or see the relevent chapter of the URL Loading System Programming Guide.
In terms of keeping track of whether you're done, as Wain suggests, you can just keep track of the number of requests issued and the number of requests completed/failed, and in your "task completion" logic, just compare these two numbers, and initiate the "all done" logic if the number of completions matches the number of requests. There are a bunch of ways of doing this, somewhat dependent upon the details of your implementation, but hopefully this illustrates the basic idea.
Instead of using GCD you should consider using NSOperationQueue. You should also limit the number of concurrent operations, certainly on mobile devices, to perhaps 4 so you don't flood the network with requests.
Now, the number of operations on the queue is the remaining count. You can add a block to the end of each operation to check the queue count and execute any completion logic.
As Rob says in his answer you might want to consider NSURLSession rather than doing this yourself. It has a number of advantages.
Other options are building your own download manager class, or using a ready-made third party framework like AFNetworking. I've only worked with AFNetworking a little bit but from what I've seen its elegant, powerful, and easy to use.
Our company wrote an async download manager class based on NSURLConnection for a project that predates both AFNetworking and NSURLSession. It's not that hard, but it isn't as flexible as either NSURLSession or AFNetworking.

Dropbox API request pattern

I am using the Dropbox SDK on iOS, and am mirroring a remote directory locally. I understand the basic usage pattern - make a request, wait for the delegate to be called with the results.
When I have a large number of requests to perform, should I serialize them by waiting for the result before making the next call, or make all requests at once and then just wait for them each to come in? Does the Dropbox SDK handle the latter case intelligently (e.g. with an NSOperationQueue), or am I better off doing this myself?
If I am better off handling request queuing myself, should I change behavior when the user is on a wifi vs. cellular connection?
EDIT: I have seen CHBgDropboxSync and other existing solutions. My app requires more control over syncing than these provide, so I need to roll my own.
Depends on how many requests you need to make and how reliant they are on each other. With either GCD or NSOperation you can daisy-chain requests, you can issue them all at once and keep semaphores in your program, or you can make requests rely on others to complete. You're creating an asynchronous state machine, and its design will depend on whether that state machine is dynamic or static.

Resources