I wonder why my delegate method -(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location not called. There is my code:
- (void)viewDidLoad {
[self createSessionWithDelegate];
[super viewDidLoad];
}
- (void)createSessionWithDelegate {
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
NSURLSessionDownloadTask *task = [session downloadTaskWithURL:[NSURL URLWithString:#"http://example.com/my-expected-image.jpg"]];
[task resume];
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
UIImage *downloadedImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:location]];
if (downloadedImage){
NSLog(#"Got image!");
}
// Perform UI changes in main thread
dispatch_async(dispatch_get_main_queue(), ^{
self.myImageView.image = downloadedImage;
});
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
NSLog(#"%f / %f", (double)totalBytesWritten, (double)totalBytesExpectedToWrite);
}
Also I want to add, that my method : -(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite works correctly (it shows output how much bytes I got).
Did you try to implement
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
to see if some error is returned?
Related
I am developing an app where I need to download a video and then perform some editing on it.So, I am downloading a video using NSURLSession and when I Have downloaded it,I close the app and then open it again.Now instead of using that downloaded video,I need to check if its already downloaded and then get the URL.Here is the code that I have been using:
-(void)startDownloadingWithURLString:(NSString*)urlString{
NSURL *url = [NSURL URLWithString:urlString];
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
self.urlSession = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:[NSOperationQueue mainQueue]];
task = [self.urlSession downloadTaskWithURL:url];
[task resume];
}
#pragma mark - NSURLSession delagate methods
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSFileManager *fileManager_ = [NSFileManager defaultManager];
NSURL *url = [NSURL URLWithString:[path stringByAppendingPathComponent:#"video.mp4"]];
if([fileManager_ fileExistsAtPath:[location path]]){
[fileManager_ replaceItemAtURL:url withItemAtURL:location backupItemName:nil options:NSFileManagerItemReplacementUsingNewMetadataOnly resultingItemURL:nil error:nil];
_videoURLPath = url;
}
UISaveVideoAtPathToSavedPhotosAlbum([url path
], nil, nil, nil);
dispatch_async(dispatch_get_main_queue(), ^{
self.progressView_.progress = 0.0;
});
}
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
NSLog(#"%f",(double)totalBytesWritten/(double)totalBytesExpectedToWrite);
dispatch_async(dispatch_get_main_queue(), ^{
self.progressView_.progress = (double)totalBytesWritten/(double)totalBytesExpectedToWrite;
});
}
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
[KSToastView ks_showToast:#"Download complete"];
NSData *data = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:_videoURLPath.path]];
[AppHelper saveToUserDefaults:_videoURLPath.path withKey:#"videoURL"];
dispatch_async(dispatch_get_main_queue(), ^{
SAEditUploadViewController *editVC = [self.storyboard instantiateViewControllerWithIdentifier:#"SAEditUploadViewController"];
editVC.videoPathData = data;
[self.navigationController pushViewController:editVC animated:YES];
});
}
what I want to do is when I send message to -(void)downloadTaskStartfrom another class then get the bytesWritten value (I use singleton), It'll return the value after the task finished(either with error or not), not the value at very first milisecond. Thanks to any help!
#interface DownloadManager ()
#property (nonatomic, strong) NSString *bytesWritten;
#end
#implementation DownloadManager
- (void)downloadTaskStart {
NSURL *url = [NSURL URLWithString:#"http://somehost.com/somefile.zip];
NSMutableURLRequest *downloadRequest = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:60];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *downloadSession = [NSURLSession sessionWithConfiguration:config
delegate:self
delegateQueue:[NSOperationQueue mainQueue]];
downloadTask = [downloadSession downloadTaskWithRequest:downloadRequest];
startTime = [NSDate timeIntervalSinceReferenceDate];
[downloadTask resume];
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
if (error) {
NSLog(#"Error when download: %#", error);
}
//do something?
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
//do something?
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
self.bytesWritten = [#(totalBytesWritten) stringValue];
}
#end
You can use
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
method of NSURLSession as it will be called when task is finished and in case of error and to pass the value use Delegates or NSNotification
Some background first:
Application is supposed to grab files from AWS S3 server. In order to do that, first step of that process is to go to local server and get the name of the file and some other information from it. After that step we have a complete URLMutableRequest.
NOTE: I am setting up the NSURLSession as a background session:
- (NSURLSession *)backgroundSession
{
static NSURLSession *session = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:#"identifier"];
session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
});
return session;
}
This is the task to download the files from AWS S3 server:
for this task I want to use the delegates to run in background mode.
#property (nonatomic, strong) NSURLSessionDownloadTask *downloadTask;
#property (nonatomic, strong) NSURLSession *defaultSession;
self.defaultSession = [self backgroundSession];
self.downloadTask = [self.defaultSession downloadTaskWithRequest:request];
[self.downloadTask resume];
How to I get a RESPONSE form this REQUEST?
Apple documentation says you can't have a block as completionHandler when using a backgroundSessionConfiguration.
In case anyone wondering how to get download response before download is complete, try this: fire off dataTask instead, get the response, then convert dataTask to download if required.
NSURLSessionTask *task = [session dataTaskWithRequest:request];
[task resume];
NSURLSessionDataDelegate
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
// use response, convert data task to download task
completionHandler(NSURLSessionResponseBecomeDownload);
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask {
// downloadTask converted from dataTask
}
NSURLSessionDownloadDelegate
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
// update progress
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
// copy downloaded file from location
}
NSURLSessionDownloadTask has a response property (part of its base class, NSURLSessionTask) that should be set to the response. See here.
You need to implement the NSURLSessionDownloadDelegate protocol in your class (since you specified the sessions delegate as self).
You should check the docs for the available methods, but you're going to implement at least the following :
- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
My objective is to send data/image to the server when the app is in background. From iOS7, we can do this using background fetch. But the background fetch only offers 30 sec time limit. The data which I am sending to the server may take longer time since it has more images. While googling I came across Background Transfer Service which offers unlimited time to upload/download data in the background. Is it possible to initiate the background transfer service in the background fetch code? If so how to handle it.
Whenever you want to start your upload/download (in your case during your 30secs of background fetch) execute the following lines:
NSString *downloadURLString = //Your link here;
NSURL* downloadURL = [NSURL URLWithString:downloadURLString];
NSURLRequest *request = [NSURLRequest requestWithURL:downloadURL];
// Create a background session
static NSURLSession *session = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *identifier = #"com.yourcompany.yourapp";
NSURLSessionConfiguration* sessionConfig = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:identifier];
session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
});
//Init a NSURLSessionDownloadTask with the just-created request and resume it
NSURLSessionDownloadTask *task = [session downloadTaskWithRequest:request];
[task resume];
});
Also, don't forget to implement those delegate methods:
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location;
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite;
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes;
For a detailed sample, have a look at this sample app
I am downloading multiple files using NSURLSession, I want to save downloaded data even on cancelling download task and on resume want to start download for the remaining data.
My
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
is not getting called.
This My Code to download:
sessionConfig =[NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:#"Download Manager"];
sessionConfig.allowsCellularAccess=!_UseOnlyWiFi;
sessionConfig.HTTPMaximumConnectionsPerHost=1;
queue.maxConcurrentOperationCount=1;
session =[NSURLSession sessionWithConfiguration:sessionConfig
delegate:self
delegateQueue:[NSOperationQueue mainQueue]];
NSURLSessionDownloadTask *downloadTask=[session downloadTaskWithURL:dwurl ];
[downloadTask resume];
`you have implemented datatask delegate but created download task implement this`
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite;
instead of
(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
Maybe you can try using
cancelByProducingResumeData:
It says on Apple documentation:
Cancels a download and calls a callback with resume data for later use.