I use NSURLSession, and NSURLSessionDownloadTask to download o file. During the downloading time, I kill the application (by tapping on home button, and swipe off the application). I received an error "Cancel" in the following function as I expected:
#property (nonatomic, strong) NSURLSessionDownloadTask *downloadTask;
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
....
self.downloadTask = nil;
...
}
the userinfo of the error contains the resume data, in order that later we can continue with downloading. However, I don't want that. What I want is when I re-open the application, it starts downloading again, but not resume to download.
I assign the self.downloadTask = nil, but it does not resolve the problem.
The following function I used to start downloading:
-(void) download:(NSURLRequest*) request {
if (self.downloadTask != nil) return;
self.session = [self sharedBackgroundSession];
self.downloadTask = [self.session downloadTaskWithRequest:request];
[self.downloadTask resume];
}
- (NSURLSession *)sharedBackgroundSession {
static NSURLSession *session = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:#"abs.com.DownloadApp"];
session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
});
return session;
}
Note: I am using XCode6, Objective-C
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;
}
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 want to do upload the data that downloaded from the server in the share extension, but the upload task is delayed, it will proceed in the next time I push post button in my share extension. I found this message in Document describes "discretionary", does it mean that the upload task is started when the APP is in the background status, so the system might delay do upload.
For transfers started while your app is in the background, the system always starts transfers at its discretion—in other words, the system assumes this property is YES and ignores any value you specified.
Here are the codes:
- (void)didSelectPost {
NSURLSession *session = [self configureSession];
NSString *downLoadStr = [NSString stringWithFormat:#"https://apis.live.net/v5.0/%#/content?access_token=%#", [shareProfile objectForKey:#“PATH”], accesToken];
mDownloadTask = [session downloadTaskWithURL:downUrl];
[mDownloadTask resume];
[self.extensionContext completeRequestReturningItems:[self.extensionContext inputItems] completionHandler:nil];
}
- (NSURLSession *) configureSession {
static NSURLSession *session = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSURLSessionConfiguration* config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:#"com.test.background_shareext"];
// To access the shared container you set up, use the sharedContainerIdentifier property on your configuration object.
config.sharedContainerIdentifier = #"group.test.background”;
session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
});
return session;
}
- (void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
if (location != nil) {
NSString *str = [NSString stringWithContentsOfFile:[location path] encoding:NSUTF8StringEncoding error:nil];
char *utfString = [[#"" stringByAppendingFormat:#"%#\n%#\n", str, #"test"] UTF8String];
NSData *uploadData = [NSData dataWithBytes:utfString length:strlen(utfString)];
NSString *uploadUrlStr = [NSString stringWithFormat:#"https://apis.live.net/v5.0/%#/files/%#?suppress_response_codes=true&overwrite=true&access_token=%#", path, file, accesToken];
NSURL *uploadUrl = [NSURL URLWithString:uploadUrlStr];
NSMutableURLRequest *uploadReq = [NSMutableURLRequest requestWithURL:uploadUrl cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:HTTP_REQUEST_TIMEOUT_INTERVAL];
[uploadReq setHTTPMethod:#"PUT"];
[uploadReq setHTTPBody:uploadData];
NSURLSession *session = [self configureSession];
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithStreamedRequest:uploadReq];
[uploadTask resume];
}else{
NSLog(#"download task location is nil");
}
}
Update:
After I share some times, the download task might stop, and runs until I share later.
I am using NSURLSession for background image uploading. And according to uploaded image my server gives me response and I do change in my app accordingly. But I can't get my server response when my app uploading image in background because there is no completion block.
Is there way to get response without using completion block in NSURLUploadTask?
Here is my code :
self.uploadTask = [self.session uploadTaskWithRequest:request fromData:body completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSString *returnString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"returnString : %#",returnString);
NSLog(#"error : %#",error);
}];
[self.uploadTask resume];
But i got this error..
Terminating app due to uncaught exception 'NSGenericException', reason: 'Completion handler blocks are not supported in background sessions. Use a delegate instead.'
But if I can't use completion handler than how should I get the server response. It says use delegate but I can't find any delegate method which can gives me server response.
A couple of thoughts:
First, instantiate your session with a delegate, because background sessions must have a delegate:
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:kSessionIdentifier];
self.session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
Second, instantiate your NSURLSessionUploadTask without a completion handler, because tasks added to a background session cannot use completion blocks. Also note, I'm using a file URL rather than a NSData:
NSURLSessionTask *task = [self.session uploadTaskWithRequest:request fromFile:fileURL];
[task resume];
Third, implement the relevant delegate methods. At a minimum, that might look like:
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
NSMutableData *responseData = self.responsesData[#(dataTask.taskIdentifier)];
if (!responseData) {
responseData = [NSMutableData dataWithData:data];
self.responsesData[#(dataTask.taskIdentifier)] = responseData;
} else {
[responseData appendData:data];
}
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
if (error) {
NSLog(#"%# failed: %#", task.originalRequest.URL, error);
}
NSMutableData *responseData = self.responsesData[#(task.taskIdentifier)];
if (responseData) {
// my response is JSON; I don't know what yours is, though this handles both
NSDictionary *response = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:nil];
if (response) {
NSLog(#"response = %#", response);
} else {
NSLog(#"responseData = %#", [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]);
}
[self.responsesData removeObjectForKey:#(task.taskIdentifier)];
} else {
NSLog(#"responseData is nil");
}
}
Note, the above is taking advantage of a previously instantiated NSMutableDictionary called responsesData (because, much to my chagrin, these "task" delegate methods are done at the "session" level).
Finally, you want to make sure to define a property to store the completionHandler provided by handleEventsForBackgroundURLSession:
#property (nonatomic, copy) void (^backgroundSessionCompletionHandler)(void);
And obviously, have your app delegate respond to handleEventsForBackgroundURLSession, saving the completionHandler, which will be used below in the URLSessionDidFinishEventsForBackgroundURLSession method.
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler {
// This instantiates the `NSURLSession` and saves the completionHandler.
// I happen to be doing this in my session manager, but you can do this any
// way you want.
[SessionManager sharedManager].backgroundSessionCompletionHandler = completionHandler;
}
And then make sure your NSURLSessionDelegate calls this handler on the main thread when the background session is done:
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
if (self.backgroundSessionCompletionHandler) {
dispatch_async(dispatch_get_main_queue(), ^{
self.backgroundSessionCompletionHandler();
self.backgroundSessionCompletionHandler = nil;
});
}
}
This is only called if some of the uploads finished in the background.
There are a few moving parts, as you can see, but that's basically what's entailed.
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;
}