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.
Related
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
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.
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.
Currently I'm working on implementing a network manager for handling download and upload tasks. I have a class that confirms to URLSessionDownloadDelegate, URLSessionDelegate. The problem I'm facing is I'm using a single session object which is used for all the service calls. So when multiple network operations are being processed, all the response call backs will be handled in the class that is implementing the delegate methods. So to find for which call a response has been arrived, I'm comparing the task parameter of the delegate method and all the tasks that are running currently(I have closure property for each of the delegate methods in the class that confirms to session protocols). Is there any other ways to achieve this result ?(I think this won't be a good solution when handling large number of requests)
Ideally, you shouldn't be doing comparisons yourself, but rather should store the closures in a dictionary keyed by the task objects. Be sure to update the dictionary in any delegate methods that replace one task with another. And be sure to do dictionary lookups and stores on the same thread or serial queue every time.
I don't have a current problem really, but rather a question that I can't seem to find a satisfying answer to.
What is good practice to use when handling callbacks and invokations of NSURLConnection in objective-c? Let's look at some examples.
Let's say I have 3 different ViewControllers, and I've created a class that does the network calls with NSURLConnection in order to obtain a JSON from some site. All 3 VC's will be doing different kinds of network calls. How would one go about doing this without having to repeat code or have the network handling all spread out? And what if I need to make more network calls depending on the result of a previous call?
I use the normal NSURLConnection delegates, such as didReceiveResponse, didReceiveData, connectionDidFinishLoading etc. So what I do is I store the statusCode in didReceiveResponse and in connectionDidFinishLoading I call a function I name handleResponse which checks the statusCode and if the statusCode states that the call was successful I translate the NSData* object that I've stored the data I received from didReceiveData delegate. This is all fine, but the data I translate I want to be sent to the correct viewController.
There are several options to do this:
I let the delegate for the NSURLConnection calls be the
ViewController itself. This sucks, since I have to duplicate the
NSURLConnection code in every VC I want to make a network call,
obviously.
Or I could handle all the data in the Network class that performs
the NSURLConnection calls. This sucks since I get logic code in a
network class, and I don't really want that? This also requires that the Network Class knows about classes and data structures that it probably shouldn't. It's a network class and should not do anything but networking stuff.
My final approach, which I try for the first time now, is to let the
ViewController tell the network class that it wants to make a
NSURLConnection call, and sets a NSNotification message which the
Network class should invoke in the handleResponse method I
mentioned earlier. The Network class sends the data it received
through that NSNotification and the logic for handling that data
falls on the VC that asked for instead. To me this is most logical
and feels like a somewhat good way of doing things.
The problem with solution 3 is that I have to have different Notification methods to handle different kinds of data, which could result in quite a few such methods. If different VC's also use the same kind of data handling as another NSNotification method, then I have to duplicate that code as well. I'm also fairly new to using NSNotification in general and not very knowledgeable about its drawbacks.
Final question, let's say I want to make 4 different NSURLConnection calls, one after the other has been confirmed finished and successful. How would you go about that? I've done this a few times, but it ends up as a counter in the form of an enum to keep track of which network call was just made and which one should be next, which results in switch-statements executing certain code depending on which was just performed. I'm thinking about maybe implementing some sort of queue which goes through all calls in a sequence instead.
I appreciate any kind of input!
Most if not all of these problems are solved by AFNetworking. Specially the problems concerning having to duplicate requests or connections.
For the final question, for example you can do it with blocks AFNetworking provides such a structure, so you can specifically coordinate multiple calls.
AFNetworking
There are a lot of tutorials out there and the brief documentation of the library github site provides short, concise and easy to understand examples. Getting to use it takes a few minutes.