NSURLSession: can't get resume data after calling cancelByProducingResumeData - ios

I want to resume downloading after user cancels downloading or something wrong happens. But when I call cancelByProducingResumeData method, the resumeData is nil. So, I can't resume downloading. I'm sure the download link can be resumed, because our PC client can resume downloading of this link.
Here is my code. Here is the full project.
#import "ViewController.h"
#interface ViewController ()
{
NSURLSession *_session;
}
#property (weak, nonatomic) IBOutlet UIProgressView *progressView;
#property NSURLSessionDownloadTask *netTask;
#property NSData *resumeData;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
if (_session == nil) {
NSURLSessionConfiguration *confi = [NSURLSessionConfiguration defaultSessionConfiguration];
_session = [NSURLSession sessionWithConfiguration:confi delegate:self delegateQueue:[NSOperationQueue mainQueue]];
}
}
- (IBAction)startAction:(id)sender {
[self start];
}
- (IBAction)stopActon:(id)sender {
[self stop];
}
- (void)stop {
__weak typeof(self) vc = self;
[self.netTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
vc.resumeData = resumeData;
vc.netTask = nil;
}];
}
- (void)start {
if (self.resumeData != nil) {
self.netTask = [_session downloadTaskWithResumeData:self.resumeData];
} else {
NSURL *downlaodURL = [NSURL URLWithString:#"http://sdl24.yunpan.cn/share.php?method=Share.download&cqid=37ef0df7c8155bacf55c237bd433ddd8&dt=24.02b6cbb4148de503fe35ddab08dac35b&e=1459317290&fhash=41181b28ff97806ef8469842b4a5eabc330a0c60&fname=feistudy%2B%2B%25E8%25AF%25AD%25E8%25A8%2580%25E5%25AD%25A6%25E4%25B9%25A0%25E6%2596%25B9%25E6%25B3%2595%25E8%25AE%25BA%25E8%25BF%25B0&fsize=87624815&nid=14471440239484082&st=e08142ab7c935cdd15ecc8851c82e819&xqid=22309244"];
self.netTask = [_session downloadTaskWithURL:downlaodURL];
}
[self.netTask resume];
}
- (NSString*)filePath
{
NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *path = [doc stringByAppendingPathComponent:#"p.rmvb"];
return path;
}
#pragma mark - NSURLSessionDownloadDelegate
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
dispatch_async(dispatch_get_main_queue(), ^{
NSFileManager *manager = [NSFileManager defaultManager];
[manager moveItemAtPath:location.path toPath:[self filePath] error:nil];
NSLog(#"locaton.path:%#", location.path);
NSLog(#"filePaht:%#",[self filePath]);
});
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
dispatch_async(dispatch_get_main_queue(), ^{
double progress = totalBytesWritten/(double)totalBytesExpectedToWrite;
NSLog(#"progress:%f",progress);
self.progressView.progress = progress;
});
}
#pragma mark - NSURLSessionTaskDelegate
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didCompleteWithError:(nullable NSError *)error
{
if (error) {
NSData *resumeData = error.userInfo[NSURLSessionDownloadTaskResumeData];
self.resumeData = resumeData;
}
}
#end

There's a long list of requirements for when resume data can be obtained. Among other things:
The response must contain an ETag or Last-Modified header.
I think the response must also contain an Accept-Ranges: bytes header
The temporary file must still exist (not low on disk space).
It must be an HTTP or HTTPS request.
The Last-Modified or ETag header must indicate that the file hasn't changed since it was last requested.
There might be other requirements that I'm forgetting, e.g. HTTP/1.1.

Related

File downloader Using NSURLSessionTask downloadTask

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;
}

NSURLSession delegate implementation returns only <head> of html

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?

NSURLSession methods don't call

I want to download a file from the server by using the NSURLSession instead of NSURLConnection. But when I use it, some of delegate method is not calling and I don't know why.
#import "ViewController.h"
#interface ViewController ()<NSURLSessionDelegate,NSURLSessionDataDelegate,NSURLSessionDownloadDelegate>
#property (strong,nonatomic) NSURLSessionDownloadTask *downloadTask;
#property (strong,nonatomic) NSURLSession *session;
#end
#implementation ViewController
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
NSURLSessionConfiguration *backgroundConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:#"com.webcash.TestingNSURLSession"];
self.session = [NSURLSession sessionWithConfiguration:backgroundConfiguration delegate:self delegateQueue:nil];
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)startDownload:(id)sender {
NSURL *downloadRUL = [NSURL URLWithString:#"http://laguboard.com/music/down/33867378/2794942/NTU0NktpZE56eERWZGxVeC9zTS9LVUd1TEhkZTE0cFZuaW1QS1pFMHVhOUNkM2ZoZEE=/(http://mp3xload.wapka.me).mp3"];
self.downloadTask = [self.session downloadTaskWithURL:downloadRUL];
[self.downloadTask resume];
}
- (IBAction)stopDownload:(id)sender {
}
// ====== THIS METHOD IS NOT CALLING
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
if (totalBytesExpectedToWrite == NSURLSessionTransferSizeUnknown) {
return;
} else {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
double progress = (double)totalBytesWritten / (double)totalBytesExpectedToWrite;
NSLog(#"Downloading progress : %f",progress);
}];
}
}
// ==============
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
if (error != nil) {
NSLog(#"Error : %#",[error localizedDescription]);
} else {
NSLog(#"Download finished");
}
}
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *urls = [fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
NSURL *documentsDirectory = [urls objectAtIndex:0];
NSURL *originalURL = [[downloadTask originalRequest] URL];
NSURL *destinationURL = [documentsDirectory URLByAppendingPathComponent:[originalURL lastPathComponent]];
[fileManager removeItemAtURL:destinationURL error:NULL];
BOOL success = [fileManager copyItemAtURL:location toURL:destinationURL error:&error];
if (success) {
NSLog(#"Download finished");
} else {
NSLog(#"Error : %#",error.description);
}
}
#end
I follow up with many tutorial but it's still not working. please help me to fix this out. thank you.!
#import "ViewController.h"
#interface ViewController ()<NSURLSessionDelegate, NSURLSessionTaskDelegate>
#property (nonatomic, strong) NSMutableData *activeDownload;
#property (nonatomic, strong) NSURLSessionTask *downloadTask;
#property (nonatomic, assign) NSInteger downloadSize;
#property (nonatomic, assign) NSInteger downloadedSize;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)startDownload:(id)sender {
self.downloadedSize = 0;
NSURL *nsurl = [NSURL URLWithString:#"http://laguboard.com/music/down/33867378/2794942/NTU0NktpZE56eERWZGxVeC9zTS9LVUd1TEhkZTE0cFZuaW1QS1pFMHVhOUNkM2ZoZEE=/(http://mp3xload.wapka.me).mp3"];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *downloadSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:nsurl];
self.downloadTask = [downloadSession dataTaskWithRequest:request];
[self.downloadTask resume];
[downloadSession finishTasksAndInvalidate];
}
- (IBAction)stopDownload:(id)sender {
[self.downloadTask cancel];
self.downloadTask = nil;
self.activeDownload = nil;
}
#pragma mark - NSURLSessionDataDelegate methods
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
completionHandler(NSURLSessionResponseAllow);
self.activeDownload = [[NSMutableData alloc] init];
NSString *contentLength = [[(NSHTTPURLResponse *)response allHeaderFields] valueForKey:#"Content-Length"];
self.downloadSize = contentLength.integerValue;
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
[self.activeDownload appendData:data];
self.downloadedSize += [data length];
float downloadProgress = ((float) self.downloadedSize / (float) self.downloadSize) * 100;
//percentage of downloading progress
}
#pragma mark - NSURLSessionTaskDelegate methods
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
if (error != nil) {
NSLog(#"Error : %#",[error localizedDescription]);
} else {
NSLog(#"Download finished");
//the data is in self.activeDownload
}
self.activeDownload = nil;
self.downloadTask = nil;
}
#end

can NSURLProtocol work with NSURLSession {upload, download}dataTask

I have a custom NSURLProtocol
#import <Foundation/Foundation.h>
#interface XXXURLProtocol : NSURLProtocol <NSURLSessionDataDelegate, NSURLSessionTaskDelegate>
#property (nonatomic, strong) NSURLSession *session;
#property (nonatomic, strong) NSURLSessionTask *task;
#end
#import "XXXURLProtocol.h"
#implementation DXYURLProtocol
+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
if ([NSURLProtocol propertyForKey:YXURLProtocolHandled inRequest:request]) {
return NO;
}
NSString *scheme = [[request URL] scheme];
NSDictionary *dict = [request allHTTPHeaderFields];
return [dict objectForKey:#"custom_header"] == nil &&
([scheme caseInsensitiveCompare:#"http"] == NSOrderedSame ||
[scheme caseInsensitiveCompare:#"https"] == NSOrderedSame);
}
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
return request;
}
+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a
toRequest:(NSURLRequest *)b {
return [super requestIsCacheEquivalent:a toRequest:b];
}
- (void)startLoading
{
NSMutableURLRequest *mutableReqeust = [[self request] mutableCopy];
//add custom headers
[XXXURLProtocol addCustomHeaders:mutableReqeust];
[NSURLProtocol setProperty:#(YES)
forKey:YXURLProtocolHandled
inRequest:mutableReqeust];
NSURLSessionConfiguration *config;
config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.protocolClasses = #[ self ];
self.session = [NSURLSession sessionWithConfiguration:config];
self.task = [self.session dataTaskWithRequest:mutableReqeust];
[self.task resume];
}
- (void)stopLoading
{
[self.task cancel];
self.task = nil;
}
//and all other NSURLProtocolClient protocol method
#end
how to make this custom NSURLProtocol to support {upload, download}dataTask?
You have the right idea, but you need to implement some key URLSessionDelegate methods to pass the response and data up to the NSURLProtocol client:
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
if( error ) {
[self.client URLProtocol:self didFailWithError:error];
} else {
[self.client URLProtocolDidFinishLoading:self];
}
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
completionHandler(NSURLSessionResponseAllow);
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
[self.client URLProtocol:self didLoadData:data];
}

how to cancel the pending download task using NSURLSession in ios?

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.

Resources