In iPad app, I need to connect to a server and download files that's using a self-signed SSL certificate with NSURLSession object in background mode:
static NSString *const URL = #"https://...";
- (void)testBackgroundDownloadTask {
if (self.downloadTask) {
return self;
}
self.session = [self backgroundSession];
NSURL *downloadURL = [NSURL URLWithString:URL];
NSURLRequest *request = [NSURLRequest requestWithURL:downloadURL];
self.downloadTask = [self.session downloadTaskWithRequest:request];
[self.downloadTask resume];
}
- (NSURLSession *)backgroundSession{
static NSURLSession *sess = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:URL];
// Default configuration: working perfectly
//NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.sessionSendsLaunchEvents = YES;
//configuration.TLSMinimumSupportedProtocol = kSSLProtocolAll;
configuration.networkServiceType = NSURLNetworkServiceTypeBackground;
sess = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
});
return sess;
}
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
NSURLProtectionSpace *protectionSpace = challenge.protectionSpace;
NSString *authMethod = protectionSpace.authenticationMethod;
if ([authMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]) {
// obtain challenge, it working with NSURLSession in default session config
[self.challengeHandler handleChallenge:challenge onCompletion:^(id obj) {
NSURLCredential *c = (NSURLCredential *)obj;
if (c) {
[challenge.sender useCredential:c forAuthenticationChallenge:challenge];
completionHandler(NSURLSessionAuthChallengeUseCredential, c);
}
else {
[challenge.sender cancelAuthenticationChallenge:challenge];
}
}];
}
else if ([authMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
}
}
If I use defaultSessionConfiguration, my app after call didReceiveChallenge method perform successfully download file (call NSURLSesionDownloadDelegate methods
– URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:
– URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:
– URLSession:downloadTask:didFinishDownloadingToURL:
But I use backgroundSessionConfiguration, after call didReceiveChallenge other delegate’s methods not called (file not downloading and didCompleteWithError not called)
Any ideas on how to resolve this problem?
Are you testing this in the background?
On your NSURLSessionDownloadDelegate, you should see what you get when you implement
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
NSLog(#"%s %# %#", __PRETTY_FUNCTION__, task.response, error);
}
If any of that stuff in –URLSession:didReceiveChallenge:completionHandler: is async, then you need to surround it with a UIApplication/UIBackgroundTaskIdentifier {begin,end}BackgroundTask..., or iOS will kill your background process when the stack pops.
Related
I want to create File download manager to download multiple files with download percent with play pause delete functionality .
I try below code to download multiple file successfully ...but unable to add progress bar please help
for (int i = 0; i < [arr_bookChapter count]; i++) {
NSURLSessionTask * downloadTask = [session downloadTaskWithURL: downloadfileUrl completionHandler: ^ (NSURL * location, NSURLResponse * response, NSError * error) {
if (error == nil) {
NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse * ) response;
if ([httpResponse statusCode] == 200) {
//download file save here
dispatch_queue_t backgroundQueue = dispatch_queue_create("dispatch_queue_#1", 0);
dispatch_async(backgroundQueue, ^ {
dispatch_async(dispatch_get_main_queue(), ^ {
// NSError *error;
//download complete here
});
});
}
} else {
//faile
}
}];
[downloadTask resume];
}
Here i got swift code:
Can someone create or or provide solution for objective-C
You can easily do this, you just need to implement these delegates in your ViewContorller.
<NSURLSessionDataDelegate, NSURLSessionDelegate, NSURLSessionTaskDelegate>
And than you need to follow this code:
#property (nonatomic, retain) NSMutableData *dataToDownload;
#property (nonatomic) float downloadSize;
- (void)viewDidLoad {
[super viewDidLoad];
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];
NSURL *url = [NSURL URLWithString: #"your url"];
NSURLSessionDataTask *dataTask = [defaultSession dataTaskWithURL: url];
[dataTask resume];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
completionHandler(NSURLSessionResponseAllow);
progressBar.progress=0.0f;
_downloadSize=[response expectedContentLength];
_dataToDownload=[[NSMutableData alloc]init];
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
[_dataToDownload appendData:data];
progressBar.progress=[ _dataToDownload length ]/_downloadSize;
}
I'm trying to load a self signed website. I know I can use NSURLConnection to give credential by using this code
UIWebView to view self signed websites (No private api, not NSURLConnection) - is it possible?
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
[challenge.sender useCredential: [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge: challenge];
}
and load the request in
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.webviewVc.webview loadRequest:_FailedRequest];
}
But when I try to use NSURLSession to do the similar thing, I'm always getting the error
NSURLConnection/CFURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813)
My code looks like this
- (BOOL)loadWithRequest:(NSURLRequest *)request
{
if (!_Authenticated ) {
_FailedRequest = request;
NSURLSessionConfiguration* defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:nil];
NSURLSessionDataTask * task = [session dataTaskWithRequest:_FailedRequest];
[task resume];
return NO;
}
_Authenticated = NO;
return YES;
}
-(void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler
{
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
[self.webviewVc.webview loadRequest:_FailedRequest];
}
Any idea?
Thanks
I am trying to use NSURLProtocol to handle authentication challenges for every NSURLSession connection . I used
[NSURLProtocol registerClass:[CustomHTTPSProtocol class]]
for NSURLConnection .But it is not working with NSURLSession so i need to setSessionConfiguration.protocolClasses=#[[CustomHTTPSProtocol class]];.
The problem is i use this URLProtocol class to only handle authentication challenges and i get received data's in the delegates of original class.
Something like this
OriginalClass
NSURLConnection
-(void)sendURL
{
[NSURLProtocol registerClass:[CustomHTTPSProtocol class]]; //done globally in didFinishLaunching
self.URL =[NSURL URLWithString:[[NSString stringWithFormat:#"https://www.google.co.in"]stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
NSMutableURLRequest *request=[[NSMutableURLRequest alloc]initWithURL:self.URL];
self.connection=[NSURLConnection connectionWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSlog(#"received data...%#",data);
}
NSURLSession
-(void)sendURL
{
self.URL =[NSURL URLWithString:[[NSString stringWithFormat:#"https://www.google.co.in"]stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
NSMutableURLRequest *request=[[NSMutableURLRequest alloc]initWithURL:self.URL];
sessionConfiguration =[NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfiguration.protocolClasses=#[[CustomHTTPSProtocol class]];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:nil];
NSURLSessionDataTask*task= [session dataTaskWithRequest:checkInInfoRequest];
[task resume];
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
NSLog(#"data ...%# ",data); //handle data here
}
NSURLProtocol Class
NSURLConnection
-(void)startLoading
{
NSMutableURLRequest *newRequest = [self.request mutableCopy];
[NSURLProtocol setProperty:#YES forKey:CustomHTTPSProtocolHandledKey inRequest:newRequest];
self.connection=[NSURLConnection connectionWithRequest:newRequest delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
{
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
}
else
{
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
}
NSURLSession
- (void) startLoading {
NSMutableURLRequest *newRequest = [self.request mutableCopy];
[NSURLProtocol setProperty:#YES forKey:CustomHTTPSProtocolHandledKey inRequest:newRequest];
NSURLSession*session=[NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
self.sessionTask=[session dataTaskWithRequest:newRequest];
[self.sessionTask resume];
}
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler{
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){
NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
}
}
Using the above logic i was able to handle authentication for all request globally and handling response individually. But with NSURLSession if i use NSURLProtocol, authentication is done in Protocol class but i am not able to receive data, since my original class delegates are not called.
Somebody help me out.
When you implement making the request yourself (or resend it as you did), your protocol becomes the challenge sender. You are responsible for creating an NSURLAuthenticationChallenge object, setting yourself as the sender, providing all the other bits from the existing challenge, and sending it using the NSURLProtocolClient class.
Take a look at Apple's CustomHTTPProtocol Sample Code project for examples and further information.
I am creating a iPhone app which download icons using NSURLSession & NSURLDownloadTask.
In this case i want to download only the visible cell icons, when i scrolled the table all pending downloadTask get canceled (i.e download task for non-visible cells is must cancel). For this i created a method terminateAllDownloads().
// terminateAllDownloadTask
-(void)terminateAllDownloads
{
NSArray *allDownloads = [self.iconDownloadInProgress allValues];
[allDownloads makeObjectsPerformSelector:#selector(cancelDownload)];
[self.iconDownloadInProgress removeAllObjects];
}
// cancelDownload
-(void)cancelDownload
{
[self.downloadTask cancel];
[self.session invalidateAndCancel];
}
and call this function in dealloc & didRecieveMemoryWarning methods of MasterViewController.m
// dealloc
-(void)dealloc
{
[self terminateAllDownloads];
}
// didReceiveMemeoryWarning
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
[self terminateAllDownloads];
}
but this method is not called single time. how should i do this ?
I am using the NSURLSession Delegates, not completion block
//code
-(void)startDownload
{
appDelgate = [[UIApplication sharedApplication] delegate];
self.fileName = self.appData.name;
self.iconURL = self.appData.iconURL;
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfig.allowsCellularAccess = NO;
_session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
self.downloadTask = [_session downloadTaskWithURL:[NSURL URLWithString:_iconURL]];
[_downloadTask resume];
}
-(void)cancelDownload
{
[self.downloadTask cancel];
[self.session invalidateAndCancel];
}
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
NSString *trimmedString = [_fileName stringByReplacingOccurrencesOfString:#" " withString:#""];
NSString *appIconDirectory = [[documentsDirectoryForAppIcons absoluteString] stringByAppendingPathComponent:#"appIcons"];
NSURL* destinationUrlForAppIcon = [[NSURL URLWithString:appIconDirectory] URLByAppendingPathComponent:[NSString stringWithFormat:#"%#%#",trimmedString, #".png"]];
NSError *error1;
if([appIconFileManager fileExistsAtPath:[destinationUrlForAppIcon absoluteString]])
{
[appIconFileManager removeItemAtPath:[destinationUrlForAppIcon absoluteString] error:NULL];
}
BOOL status = [appIconFileManager copyItemAtURL:location toURL:destinationUrlForAppIcon error:&error1];
if (status && !error1)
{
[appDelgate.downloadedIcons setValue:destinationUrlForAppIcon.path forKey:self.iconURL];
if(self.completionHandler)
{
self.completionHandler(destinationUrlForAppIcon);
}
}
else
{
NSLog(#"File copy failed: %#", [error1 localizedDescription]);
}
}
Probably you refer to self in the block passed to the NSURLSession producing a retain cycle.
I push my download view, then download file in background model, then update it progress in
delegate (uRLSession:downloadTask:didWriteData: totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:).The progressView can be updated, then pop this controller, then push it again.download file again,The UI can not be updated.
DownloadCode:
- (IBAction)download:(UIButton *)sender {
self.image.backgroundColor = [UIColor whiteColor];
NSString * downloadURLString = [NSString stringWithFormat:#"http://ww3.sinaimg.cn/mw600/bce52ee1jw1e2xe4zdqarj.jpg"];
NSURL* downloadURL = [NSURL URLWithString:downloadURLString];
NSURLRequest *request = [NSURLRequest requestWithURL:downloadURL];
NSURLSessionDownloadTask *task = [[self backgroundURLSession] downloadTaskWithRequest:request];
[task resume];
}
- (NSURLSession *)backgroundURLSession
{
static NSURLSession *session = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *identifier = #"example.demon";
NSURLSessionConfiguration* sessionConfig = [NSURLSessionConfiguration backgroundSessionConfiguration:identifier];
session = [NSURLSession sessionWithConfiguration:sessionConfig
delegate:self
delegateQueue:[NSOperationQueue mainQueue]];
});
return session;
}
ProgessView update
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
float progress = totalBytesWritten*1.0/totalBytesExpectedToWrite;
dispatch_async(dispatch_get_main_queue(),^ {
[self.progress setProgress:progress animated:YES];
});
NSLog(#"Progress =%f",progress);
NSLog(#"Received: %lld bytes (Downloaded: %lld bytes) Expected: %lld bytes.\n",
bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
}
Put below method and your code will work
-(void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[self.session invalidateAndCancel];
[self.session finishTasksAndInvalidate];
self.session = nil;
}