multiple download NSURLSession - ios

I have a program that download a video from a url using NSURLSession, but i'm not able to do multiple download at the same time.
How can i do it?
How can i manage multiple simultaneous download?
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
NSURLSessionDownloadTask *getVideo = [session downloadTaskWithURL:fileURL
completionHandler:^(NSURL *location,
NSURLResponse *response,
NSError *error) {
// 2
receivedData = [NSData dataWithContentsOfURL:location];
dispatch_async(dispatch_get_main_queue(), ^{
// do stuff with image
NSLog(#"%s receiveData:%d",__FUNCTION__,[receivedData length]);
});
}];
[getVideo resume];

From the code you have provided above you are not using any of the properties of NSURLSessionConfiguration class that would enable better download performance.
First of all I would look suggest using your own delegate queue. If you do not provide a queue then the session creates a serial operation queue for all delegate and completion handler calls see the "Creating a Session" section of the NSURLSession Class Reference document for more detail. You can look at the following properties of NSOperationQueue to help improve performance;
qualityOfService
maxConcurrentOperationCount
Next I would look at NSURLSessionConfiguration properties that may help.
HTTPMaximumConnectionsPerHost
HTTPShouldUsePipelining
Finally you should review the section "Life Cycle of a URL Session with Custom Delegates". You should confirm whether your using the delegate methods of NSURLSessionTaskDelegate and NSURLSessionDownloadTaskDelegate or just the completion handler.
You need to put more time into configuring NSURLSession to support the work you want to do.

Related

NSURLSession doesn't return data

I try to download a zip-archive using NSURLSessionDataTask.
I am aware that there is a NSURLSessionDownloadTask, but the point is I want a didReceiveData callback (to show the progress).
The code is:
NSURLRequest *request = [NSURLRequest requestWithURL:#"..."
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSOperationQueue *myQueue = [NSOperationQueue new];
myQueue.underlyingQueue = dispatch_get_main_queue();
NSURLSession *session = [NSURLSession sessionWithConfiguration:config
delegate:self
delegateQueue:myQueue];
NSURLSessionDataTask* task = [session dataTaskWithRequest:request
completionHandler:^( NSData *data, NSURLResponse *response, NSError *error){ ... }
[task resume];
My class conforms to NSURLSessionDataDelegate.
When I call the method, after several seconds debugger goes to completionHandler with nil data and nil error.
What am I doing wrong?
I also tried:
calling without completionHandler, then debugger goes to didReceiveResponse callback with 200 response and that's all.
using [NSOperationQueue new] for the queue
using [NSURLSession sharedSession] - didn't get any response
using [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier: #"..."] - falls saying that I can't use a completionHandler, but without it - also no response.
So I have found the answer and it's not quite obvious from documentation:
I had several callbacks, and among them didReceiveResponse.
Turns out I have to call completion handler in order for the future callbacks to work, i.e:
completionHandler(NSURLSessionResponseAllow);
And one more thing: didCompleteWithError is actually the delegate that tells about successful finish, too, although the name implies that this is the error handler.
What it means: when a download is successfully finished, this function is called with error = nil.
Hope this will be useful for somebody someday.

Network calls take more time on iOS than on Android

I have a very strange problem, in Android, the API calls take 300-500 ms while in iOS it takes 1.5-2.5 sec. I have removed dependencies like my server, device specific issue, internet connectivity etc. I have a very simple sample code hitting a sample URL and for me, it takes about 2 sec, even on the simulator.
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(#"start");
// 1
NSURL *url = [NSURL URLWithString:#"https://httpbin.org/get"];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
// 2
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setValue:#"" forHTTPHeaderField:#"Accept-Encoding"];
request.HTTPMethod = #"GET";
[[session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// Handle response here
NSLog(#"end - %ld", (long)[(NSHTTPURLResponse*)response statusCode]);
}] resume];
});
I also have tried using AFNetworking and ASSIHTTP libraries, but there is no difference. I also have checked the headers and they are the same in both Android and iOS. What am I doing something wrong here?
I think your problem is not in the network but in this line:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
Can you remove it and check again?
Data task is async so you don't need to wrap it in another async block.
Also you don't need to create instance of NSURLSession for every request
You can log out the timestamp (NSLog already did) when the request generate, send, callback to analyse it.
Does the server side code do any processing based on 'user-agent'?
Is there a time difference if you open the url in iOS safari and within the app ?
You can try calling the api from postman (or another REST API test tool like firefox RESTClient) and override the user-agent to use iOS values (http://www.enterpriseios.com/wiki/UserAgent). If the time difference is still the same, theres nothing you can do in your mobile code to fix this lag.
P.S. :
1. Overriding user-agent in postman needs some tweaking : https://github.com/postmanlabs/postman-app-support/wiki/Postman-Proxy

IOS - Handling Token Authentication in HTTP headers

My organization has a number of different authentication methods used. One that I have been struggling with has been our apps that use Oath2 that will be expecting the Authorization header to have the token information to be stored in the HTTP Headers. I can manually put the Authorization header information for each request, but I would like this information to be automatically populated instead of manually adding it for every NSURLSession, NSURLConnection, UIWebView or [UIImage: imageFromUrl]. In Apples documentation for NSURLConnection class and NSURLSession it says that it will handle are designed to handle various aspects >of the HTTP protocol for you, including the Authentication header and they recommend not to alter this property, but it does not seem to get set for me. At a minimum I would expect that for the NSURLSession object I should only need to set it once by accessing the sharedSession and adding the header info via a setHTTPAdditionalHeaders call, but it does not seem to hold the info on the next time I access the sharedSession.
I there something I am missing here or do I need to manually populate the HTTP Header on all calls?
EDIT:
Below is a code snippet showing how I try to set the header for the session.
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
id jSONObj = [defaults objectForKey: kTokenInformation];
[config setHTTPAdditionalHeaders:#{#"Authorization": [NSString stringWithFormat:#"Bearer %#",[jSONObj valueForKeyPath:#"access_token"]]}];
session = [NSURLSession sessionWithConfiguration:config];
However, it doesn't appear that the token is present the next time I call [NSURLSession sharedSession]. So right now I have to do that every time I call the shared session. I also am unsure from the documentation how you would handle apps that maintain multiple sessions which may each require separate Auth tokens. Any thoughts what I am missing hear.
Have a look at NSURLSessionConfiguration.
For example, you could set up a singleton class that provides an NSURLSession:
// Session setup
NSString *tokenString = ...;
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
[sessionConfiguration setHTTPAdditionalHeaders:#{ #"X-Auth-Token" : tokenString" }];
self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
Now, every time you need to make a request you use your session:
// Send Request
NSURLSession *session = [[SessionManager sharedManager] session];
[[session dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
}] resume];
Here's a great tutorial: http://code.tutsplus.com/tutorials/networking-with-nsurlsession-part-1--mobile-21394

Why does my NSURLSession dataTask make multiple calls to the server?

I have a NSURLSession dataTask created using the following method:
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate:hvc delegateQueue: [NSOperationQueue mainQueue]];
NSURLSessionDataTask * dataTask = [defaultSession dataTaskWithURL:url ];
[dataTask setAccessibilityLabel:#"UserProfiling"];
[dataTask resume];
Here, hvc is another ViewController where I am handling the delegate for this server data task call.
I see that the dataTask is making multiple calls to the server continuously.
Is there a way to ensure that only one call is made?
This has been solved and answered below.
I am answering my own question here.
By mistake, I was writing the above code in another method which was being called repeatedly. It was the Corelocation framework that was continuously updating the user's location.

Application freeze while executing fetch request in iOS

I am requesting an API that takes some time to give response, so during that time no other operation can be performed. e.g. back button or Tabs are not pressed. I am Using the following code:
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];
NSURL * url = [NSURL URLWithString:urlString];
NSURLSessionDataTask * dataTask = [defaultSession dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if(error == nil)
{
}else{
}
}];
[dataTask resume];
Can anyone Suggest why my app is freezing during this operation. Thanks in advance.
Because you are performing operation in main thread, you need to do this task in background thread.
For this you can use NSOperationQueue and add a operation of you api calling.
see belo links
NSOperation and NSOperationQueue working thread vs main thread
How To Use NSOperations and NSOperationQueues
Working with the NSOperationQueue Class
or you can also use DispatchQueue
see : Multithreading and Grand Central Dispatch on iOS for Beginners Tutorial
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// simply call your function here
});
you are doing operation on main thread swhich interrupt app execution. you should do this operation in background through GCD by creating async request to download data in background it will not interrupt your app execution.
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentQueue, ^{
NSURL * url = [NSURL URLWithString:urlString];
NSData *response = [[NSData alloc] initWithContentsOfURL:url];
// above code will download data in background
dispatch_async(dispatch_get_main_queue(), ^{
// here you can access main thread of application and do something here
});
});

Resources