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.
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 am using this custom class to download data from Server(ASP.NET IIS 7.5) using a certificate. It works just fine except that on every request the didReceiveChallenge method is called every time and credentials has to be applied again and again. The performance suffers greatly.
How can we avoid this?
Thanks.
#import "ServerRequest.h"
#import <UIKit/UIKit.h>
#implementation ServerRequest{
NSMutableData * recievedData;
NSString * operationName;
NSString * operationParameters;
DataOperationType operationType;
NSURL* downloadURL;
NSData * HTTPBodyData;
UIView * waitView;
UIProgressView * progressView;
}
- (instancetype)init{
if(self = [super init]) {
}
return self;
}
- (NSURLSession *)normalSession{
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLProtectionSpace * protectionSpace = [[NSURLProtectionSpace alloc] initWithHost:[Config getBaseURL] port:443 protocol:#"https" realm:nil authenticationMethod:NSURLAuthenticationMethodClientCertificate];
[configuration.URLCredentialStorage setDefaultCredential:[Helper getCredential] forProtectionSpace:protectionSpace];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:[NSOperationQueue mainQueue]];
return session;
}
- (instancetype)initWithRequest:(NSString *)request
{
if(self = [super init]) {
operationType = DataOperationTypeData;
downloadURL = [NSURL URLWithString:request];
NSLog(#"Created download operation for URL: %#", [downloadURL absoluteString]);
}
return self;
}
- (instancetype)initWithRequest:(NSString *)request Dicionary:(NSString *) dataDictionary{
if(self = [super init]) {
operationType = DataOperationTypeHTMLFrom;
downloadURL = [NSURL URLWithString:request];
NSLog(#"Created download operation for URL: %#", [downloadURL absoluteString]);
operationParameters = dataDictionary;
}
return self;
}
- (instancetype)initWithRequest:(NSString *)request data:(NSData *) bodyData{
if(self = [super init]) {
operationType = DataOperationTypeBodyData;
downloadURL = [NSURL URLWithString:request];
NSLog(#"Created download operation for URL: %#", [downloadURL absoluteString]);
HTTPBodyData = bodyData;
}
return self;
}
- (void) performOperationWithCompletion:(ServerRequestCompletion)onCompletion error:(ServerRequestError)onError{
if (self.showProgressView){
[self configureProgressView];
}
if (self.parentView) {
waitView = [Config createWaitView:self.parentView];
}
self.completion = onCompletion;
self.error = onError;
NSMutableURLRequest *request;
request = [NSMutableURLRequest requestWithURL:downloadURL];
[request setHTTPMethod:#"POST"];
[request setValue:#"utf-8" forHTTPHeaderField:#"Accept-Charset"];
if (operationType == DataOperationTypeHTMLFrom) {
NSData *postData = [operationParameters dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPMethod:#"POST"];
[request setValue:#"utf-8" forHTTPHeaderField:#"Accept-Charset"];
[request setValue:[NSString stringWithFormat:#"%ld", (unsigned long)postData.length] forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
}
else if (operationType == DataOperationTypeBodyData) {
[request setHTTPMethod:#"POST"];
[request setValue:[NSString stringWithFormat:#"%ld", (unsigned long)HTTPBodyData.length] forHTTPHeaderField:#"Content-Length"];
[request addValue: #"multipart/form-data; charset=utf-8" forHTTPHeaderField: #"Content-Type"];
[request setHTTPBody:HTTPBodyData];
}
else{
[request setValue:#"application/x-www-form-urlencoded charset=utf-8" forHTTPHeaderField:#"Content-Type"];
}
NSURLSession * defaultSession = [self normalSession];
NSURLSessionTask * dataTask = [defaultSession dataTaskWithRequest:request];
if (!dataTask) {
self.error([EXError errorWithDomain:[[NSBundle mainBundle] bundleIdentifier] code:-1 userInfo:#{NSLocalizedDescriptionKey: #"Could not initialize connection"}]);
}else{
if (self.showProgressView) {
dispatch_async(dispatch_get_main_queue(), ^{
// AppDelegate_Shared * appDelegate = (AppDelegate_Shared *)([UIApplication sharedApplication]).delegate;
// [appDelegate.progressView setHidden:NO];
// [appDelegate.progressView setProgress:0];
});
}
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[dataTask resume];
}
}
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler{
//NSURLProtectionSpace *protectionSpace = [challenge protectionSpace];
NSURLCredential *credential = [Helper getCredential];
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodNTLM])
{
if (challenge.previousFailureCount >= 3)
{
// handle the fact that the previous attempt failed
EXError* error = [EXError errorWithDomain:NSURLErrorDomain code:403 userInfo: #{NSLocalizedDescriptionKey: #"Unable to verify account information."}];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
self.error(error);
}else{
NSURLCredentialPersistence persistence = NSURLCredentialPersistenceForSession;
NSString * userName = [Config getUserName];
NSString * password = [Config getPassword];
NSURLCredential *loginCredential = [NSURLCredential credentialWithUser:userName password:password persistence:persistence];
completionHandler(NSURLSessionAuthChallengeUseCredential, loginCredential);
[self removeWaitView];
}
}
else if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
{
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
//completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
[self removeWaitView];
}
else if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodClientCertificate)
{ NSLog(#"\n______________________________________________________ Authentication Method Client Certificate _________________________________________________");
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
[self removeWaitView];
}
else{
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
[self removeWaitView];
}
}
-(void) configureProgressView{
UIViewController * topController = [Helper currentTopViewController];
if (![topController isKindOfClass:[UINavigationController class]]) {
return;
}
UINavigationController * navigationController = (UINavigationController *)topController;
UINavigationBar * navigationBar = [navigationController navigationBar];
progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleBar];
progressView.frame = CGRectMake(navigationBar.frame.origin.x, navigationBar.frame.size.height - 5, navigationBar.frame.size.width, 5);
[navigationBar addSubview:progressView];
}
-(void) removeWaitView{
[waitView removeFromSuperview];
waitView = nil;
}
- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error{
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
NSDictionary *responseHeaders = ((NSHTTPURLResponse *)response).allHeaderFields;
NSLog(#"headers: %#", responseHeaders.description);
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (httpResponse.statusCode >= 400) { // Client/Server Error
EXError * error = [EXError errorWithDomain:[[NSBundle mainBundle] bundleIdentifier] code:httpResponse.statusCode userInfo:#{NSLocalizedDescriptionKey: [NSHTTPURLResponse localizedStringForStatusCode:httpResponse.statusCode]}];
self.error(error);
NSLog(#"Failed with Error %ld : %#", (long)httpResponse.statusCode, [NSHTTPURLResponse localizedStringForStatusCode:httpResponse.statusCode]);
completionHandler(NSURLSessionResponseAllow);
[self removeWaitView];
return;
}
}
completionHandler(NSURLSessionResponseAllow);
[self removeWaitView];
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
if (!recievedData) {
recievedData = [NSMutableData new];
}
[recievedData appendData:data];
if ([dataTask countOfBytesExpectedToReceive] !=NSURLSessionTransferSizeUnknown) {
double progress = (double)[dataTask countOfBytesReceived] / (double) [dataTask countOfBytesExpectedToReceive];
if (self.showProgressView) {
dispatch_async(dispatch_get_main_queue(), ^{
[progressView setProgress:progress animated:YES];
});
}
}
// DLog(#"progress : %lld", [dataTask countOfBytesExpectedToReceive]);
}
//- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
// didSendBodyData:(int64_t)bytesSent
// totalBytesSent:(int64_t)totalBytesSent
// totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
// // DLog(#"Did Sent %f", (double)totalBytesSent);
//
// if (self.showProgressView) {
//
// dispatch_async(dispatch_get_main_queue(), ^{
//
// AppDelegate_Shared * appDelegate = (AppDelegate_Shared *)([UIApplication sharedApplication]).delegate;
// [appDelegate.progressView setProgress:
// (double)totalBytesSent /
// (double)totalBytesExpectedToSend animated:YES];
//
// });
// }
//}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
if (self.showProgressView) {
dispatch_async(dispatch_get_main_queue(), ^{
[progressView setHidden:YES];
});
}
// NSString *myString = [[NSString alloc] initWithData:recievedData encoding:NSUTF8StringEncoding];
//
// NSLog(#"%#", myString);
if(error == nil)
{
// SBJSON *json = [SBJSON new];
// NSString *responseString = [[NSString alloc] initWithData:recievedData encoding:NSUTF8StringEncoding];
//
// //NSLog(#"Finished (%#) %d bytes. %#", [downloadURL absoluteString], [data length], responseString);
//
// NSDictionary *dataSet = [json objectWithString:responseString];
NSDictionary* dataSet = [NSJSONSerialization
JSONObjectWithData:recievedData
options:kNilOptions
error:&error];
self.completion(dataSet);
}
else{
// if (error.code == -999) {
// return;
// }
// NSString * httpBody = [[NSString alloc] initWithData:task.currentRequest.HTTPBody encoding:NSUTF8StringEncoding];
//
// httpBody = [NSString stringWithFormat:#"%s\n%#", __PRETTY_FUNCTION__, httpBody];
// [Helper MsgBox:httpBody];
EXError * exError = [EXError errorWithDomain:error.domain code:error.code userInfo:#{NSLocalizedDescriptionKey: [NSHTTPURLResponse localizedStringForStatusCode:error.code]}];
self.error(exError);
// DLog(#"Error %#", [error localizedDescription]);
}
}
#end
One of the reasons can be a check of HTTPS the certificate.
Apple Documentation
This instance contains a protectionSpace property whose authenticationMethod property indicates the kind of challenge being issued (such as a request for a username and password, or a client certificate). You use this value to determine whether you can handle the challenge.
Adding this precondition can check the authentication method and call the handler.
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
completionHandler(NSURLSessionAuthChallengeUseCredential, nil);
return;
}
You can find authentication methods constants here
Let's go straight into code.
First in appDelegate, I register my connection protocol:
[NSURLProtocol registerClass:[MyURLConnection class]];
Here's what my connection class looks like:
+(BOOL)canInitWithRequest:(NSURLRequest *)request {
return YES;
}
-(void)startLoading {
if (self.cachedResponse) {
NSLog(#"there is a cached response %#", self.cachedResponse);
}
MyNetworkSession *task = [MyNetworkSession new];
task.request = self.request;
task.urlClient = self.client;
task.isDataTask = YES;
task.isBackgroundSession = YES;
[task start];
}
-(void)stopLoading {
// nothing here
}
Now, MyNetworkSession is the delegate for NSURLSession tasks, like so:
// ----------- MyNetworkSession.h
#interface MyNetworkSession : NSObject
<NSURLSessionDelegate,
NSURLSessionTaskDelegate,
NSURLSessionDataDelegate>
-(void) start;
#end
//------------ MyNetworkSession.m
#implementation MyNetworkSession {
NSURLSessionDataTask *_dataTask;
NSURLSessionDownloadTask *_downloadTask;
}
// create session
-(NSURLSession*) session {
static NSURLSession *session = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSURLSessionConfiguration *backgroundConfiguration = [
NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:
BACKGROUND_SESSION_IDENTIFIER];
backgroundConfiguration.requestCachePolicy = NSURLRequestUseProtocolCachePolicy;
session = [NSURLSession
sessionWithConfiguration:backgroundConfiguration
delegate:self
delegateQueue:nil];
});
return session;
}
// start download/data task
-(void)start {
if (self.isDataTask){
_dataTask = [self.session dataTaskWithRequest:self.request];
[_dataTask resume];
}
}
#pragma mark- delegate methods
-(void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error {
if (error) {
NSLog(#"Error in connection %# for task %#", error, task.response.URL);
}
NSLog(#"completed response %#", task.response.URL.absoluteString);
_dataTask = nil;
_downloadTask = nil;
[self.urlClient URLProtocolDidFinishLoading:self.urlClient];
}
-(void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
NSLog(#"received response %#", response.URL.absoluteString);
[self.urlClient URLProtocol:self.urlClient
didReceiveResponse:responsecacheStoragePolicy:NSURLCacheStorageAllowedInMemoryOnly];
completionHandler(NSURLSessionResponseAllow);
}
-(void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data {
NSLog(#"received data %#", dataTask.response.URL.absoluteString);
if (!self.data) {
self.data = [data mutableCopy];
} else {
[self.data appendData:data];
}
[self.urlClient URLProtocol:self.urlClient didLoadData:self.data];
}
//... some more methods
#end
That's all the relevant code I guess.
The problem is that when I see the response in a webview, I only get the <head> tag of the document.
I am not getting the rest of the html. And also, the js/css files listed in the <head> tag are not downloaded. Do you see any flaw in the implementation?
I'm using 'AFNetworking', '2.0.0' for download data.
I need to download large file. When user lock screen or press home button it should go pause (or continue downloading in background) and if I return to app it should resume.
Also I need to show progress of downloading.
I search a lot of examples but don't find anything 'AFNetworking', '2.0.0'.
I create app for iOS version 6.0+, so I can't use AFHTTPSessionManager or AFURLSessionManager.
For downloading in background on iOS 7 or higher i'am using NSURLSession with their NSURLSessionDownloadTask.
Switch on BackgroundMode in ProjectNavigator->YourProject->YourTarget->Capabilities (tab)->Background Modes
Add to your AppDelegate method - (BOOL)application:(UIApplication* )application didFinishLaunchingWithOptions:(NSDictionary* )launchOptions an initialiser for NSURLSessin with next code:
NSURLSessionConfiguration *sessionConfiguration;
NSString *someUniqieIdentifierForSession = #"com.etbook.background.DownloadManager";
if ([[[[[UIDevice currentDevice] systemVersion] componentsSeparatedByString:#"."] firstObject] integerValue] >= 8) {
//This code for iOS 8 and greater
sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:someUniqieIdentifierForSession];
} else {
this code for iOS 7
sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:someUniqieIdentifierForSession];
}
sessionConfiguration.HTTPMaximumConnectionsPerHost = 5;
self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration
delegate:self
delegateQueue:[NSOperationQueue mainQueue]];
Of course, don't forget to declare session property with:
#property (nonatomic, strong) NSURLSession session;
Also add completion handler property (it will be needed if you want to get callback of background download process after Application termination or crash):
#property (nonatomic, copy) void(^backgroundTransferCompletionHandler)();
Add delegate methods in AppDelegate:
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *destinationFilename = downloadTask.originalRequest.URL.lastPathComponent;
NSURL *destinationURL = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:destinationFilename];
if ([fileManager fileExistsAtPath:[destinationURL path]]) {
[fileManager removeItemAtURL:destinationURL error:nil];
}
[fileManager copyItemAtURL:location
toURL:destinationURL
error:&error];
}
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
if (error != nil) {
NSLog(#"Download completed with error: %#", [error localizedDescription]);
}
else{
NSLog(#"Download finished successfully.");
}
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
if (totalBytesExpectedToWrite == NSURLSessionTransferSizeUnknown) {
NSLog(#"Unknown transfer size");
}
else{
NSLog(#"progress = %lld Mb of %lld Mb", totalBytesWritten/1024/1024, totalBytesExpectedToWrite/1024/1024);
}
}
-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session{
ETBAppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
// Check if all download tasks have been finished.
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
if ([downloadTasks count] == 0) {
if (appDelegate.backgroundTransferCompletionHandler != nil) {
// Copy locally the completion handler.
void(^completionHandler)() = appDelegate.backgroundTransferCompletionHandler;
// Make nil the backgroundTransferCompletionHandler.
appDelegate.backgroundTransferCompletionHandler = nil;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// Call the completion handler to tell the system that there are no other background transfers.
completionHandler();
// Show a local notification when all downloads are over.
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.alertBody = #"All files have been downloaded!";
[[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
}];
}
}
}];
}
//Background download support (THIS IMPORTANT METHOD APPLICABLE ONLY IN YOUR AppDelegate.m FILE!!!)
-(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler{
self.backgroundTransferCompletionHandler = completionHandler;
}
And declare that your class (in AppDelegate.h file for my example) conforms to protocol NSURLSessionDelegate like here:
#interface AppDelegate : UIResponder <UIApplicationDelegate, NSURLSessionDelegate>
Then add download task somewhere with:
NSURLSessionDownloadTask *task = [self.session downloadTaskWithURL:[NSURL URLWithString:urlString]];
task.taskDescription = [NSString stringWithFormat:#"Downloading file %#", [urlString lastPathComponent]];
[task resume];
So when your application started after termination, your session will be restored and delegate methods will be fired. Also if your application will waked up from background to the foreground your application delegate methods will be fired too.
when your app goes to background pause/stop the downloading operation and when comes in foreground resume your paused downloading operation in NSOperation queue.
Read these lines of code may help you:
AFDownloadRequestOperation *operation = [[AFDownloadRequestOperation alloc] initWithRequest:request targetPath:filePath shouldResume:YES];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
//handel completion
[operations removeObjectForKey:audioItem.filename];
}failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//handel failure
operation = [operations objectForKey:audioItem.filename];
[operation cancel];
[operations removeObjectForKey:audioItem.filename];
if (error.code != NSURLErrorCancelled) {
[self showErrorAlert];
NSLog(#"Error: %#", error);
}
}];
[operation setProgressiveDownloadProgressBlock:^(NSInteger bytesRead, long long totalBytesRead, long long totalBytesExpected, long long totalBytesReadForFile, long long totalBytesExpectedToReadForFile) {
//handel progress
NSNumber *progress = [NSNumber numberWithFloat:((float)totalBytesReadForFile / (float)totalBytesExpectedToReadForFile)];
float progress=progress.floatValue;
[self performSelectorOnMainThread:#selector(updateProgressBar:) withObject:[NSArray arrayWithObjects:audioItem, progress, nil] waitUntilDone: YES];
}];
[operation setShouldExecuteAsBackgroundTaskWithExpirationHandler:^{
// TODO: Clean up operations
NSLog(#"App has been in background too long, need to clean up any connections!");
}];
[operations setValue:operation forKey:#"key"]; // store the NSOperation for further use in application to track its status.
[downloadQueue addOperation:operation];//here downloadQueue is NSOperationQueue instance that manages NSOperations.
To Pause downloading operation use this:
[operation pause];
To resume use this sort of line:
[operation resume];
Perhaps you're better off using DTDownload:
https://github.com/Cocoanetics/DTDownload
As far as I can tell, it doesn't have anything to report progress.
Edit: there's a nice tutorial over at AppCoda, about using NSURLSessionTask:
http://www.appcoda.com/background-transfer-service-ios7/
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;
}