ios extension use NSURLSession background upload after download will delay - ios

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.

Related

Do I need to release object using delegate?

I have a task to do without ARC. Previously I didn't use it (started studying ios development recently). I have a class that represents http request, it conforms to NSURLSessionDownloadDelegate protocol. And also I have following code:
-(void)executeWithRelativeUrl:(NSString *)relativeUrl andSuccessBlock: (void(^) (NSData*))successBlock {
NSURL *url = [[NSURL alloc] initWithString:relativeUrl relativeToURL:self.baseUrl];
[self setSuccessBlock:successBlock];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:nil];
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request];
[downloadTask resume];
[request release];
[url release];
}
that creates url session and starts download task. I'm dealing with task results in following method:
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
NSData *data = [NSData dataWithContentsOfURL:location];
dispatch_async(dispatch_get_main_queue(), ^{
self.successBlock(data);
});
}
Now the question is: do I need to release session, download task and location url in the end of the last method? Or it will be done for me? I'm asking this because I created it in the first method (except for url), and as I understand the one who is responsible for releasing the object is also me. Thanks!
The Golden Rule is very simple. Did you say alloc or copy or retain? No? Then you don't need to say release (and you must not do so).
(You need to release the url and the request for that reason, and you are doing so. So memory management is now complete.)

NSURLSession: how to start downloading again, but not resume to download?

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

Using NSURLSessionDownloadTask to display an image

I was wondering if someone could help me out. I'm trying to use NSURLSessionDownloadTask to display a picture in my UIImageView if I put the image URL into my textfield.
-(IBAction)go:(id)sender {
NSString* str=_urlTxt.text;
NSURL* URL = [NSURL URLWithString:str];
NSURLRequest* req = [NSURLRequest requestWithURL:url];
NSURLSession* session = [NSURLSession sharedSession];
NSURLSessionDownloadTask* downloadTask = [session downloadTaskWithRequest:request];
}
I am not sure where to go after this.
Two options:
Use [NSURLSession sharedSession], with rendition of downloadTaskWithRequest with the completionHandler. For example:
typeof(self) __weak weakSelf = self; // don't have the download retain this view controller
NSURLSessionTask* downloadTask = [session downloadTaskWithRequest:request completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
// if error, handle it and quit
if (error) {
NSLog(#"downloadTaskWithRequest failed: %#", error);
return;
}
// if ok, move file
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *documentsURL = [fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask][0];
NSURL *fileURL = [documentsURL URLByAppendingPathComponent:filename];
NSError *moveError;
if (![fileManager moveItemAtURL:location toURL:fileURL error:&moveError]) {
NSLog(#"moveItemAtURL failed: %#", moveError);
return;
}
// create image and show it im image view (on main queue)
UIImage *image = [UIImage imageWithContentsOfFile:[fileURL path]];
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.imageView.image = image;
});
}
}];
[downloadTask resume];
Clearly, do whatever you want with the downloaded file (put it somewhere else if you want), but this might be the basic pattern
Create NSURLSession using session:delegate:queue: and specify your delegate, in which you'll conform to NSURLSessionDownloadDelegate and handle the completion of the download there.
The former is easier, but the latter is richer (e.g. useful if you need special delegate methods, such as authentication, detecting redirects, etc., or if you want to use background session).
By the way, don't forget to [downloadTask resume], or else the download will not start.

iOS8 extension background NSURLSession sandbox error

I'm trying to upload a file from a sharing extension in the Photos app, using a background NSURLSession. Because a background NSURLSession only supports an upload task using the uploadTaskWithRequest:WithFile: API, I first get the URL for the image URL that was retrieved from the extension, write the image content to the shared container, then upload the new file. It seems like NSURLSession is having permission issues, I am getting this error:
"Failed to issue sandbox extension for file file:///private/var/mobile/Containers/Shared/AppGroup/..."
I know there are a few similar posts to this but none of them are loading an url from an extension and does not show where to write the temporary file to.
Here's the code:
- (void)fetchImageURLInExtensionContext:(NSExtensionContext*) context onComplete:(void (^)()) completion
{
NSExtensionItem *item = self.extensionContext.inputItems[0];
NSItemProvider *provider = item.attachments[0];
if ([provider hasItemConformingToTypeIdentifier:#"public.jpeg"]) {
[provider loadItemForTypeIdentifier:#"public.jpeg" options:nil completionHandler:^(id<NSSecureCoding> item, NSError *error) {
NSObject *obj = item;
if ([obj isKindOfClass:[NSURL class]]) {
self.imageURL = obj;
completion();
}
}];
}
}
- (void)postImage
{
// copy file to shared container
NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:#"group.com.mytestgroup"];
NSString *writeToPath = [[containerURL path] stringByAppendingPathComponent:#"temp.jpg"];
BOOL success = [[NSData dataWithContentsOfURL:self.imageURL] writeToFile:writeToPath atomically:YES];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"https://api.imgur.com/3/image"]];
NSString *boundary = #"multipartboundary";
[request addValue:[NSString stringWithFormat:#"multipart/form-data; boundary=%#", boundary] forHTTPHeaderField:#"Content-Type"];
request.HTTPMethod = #"POST";
[request setValue:#"Client-ID my_imgur_client_id" forHTTPHeaderField:#"Authorization"];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:#"blah"];
config.sharedContainerIdentifier = #"group.com.mytestgroup";
NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
NSURLSessionTask *uploadTask = [session uploadTaskWithRequest:request fromFile:[NSURL fileURLWithPath:writeToPath]];
[uploadTask resume];
}
workaround solution: move the file from inbox to temp directory and upload from there.

NSInputStream with url coming up nil in iOS

I'm trying to set up a NSInputStream, but my input stream is comes out as nil when I step into the code. The url comes from a Dropbox account.
Getting the file through NSData after I have the url through Dropbox Chooser crashes my iPhone 4 (although not when it is running through XCode). The files are just too big, so I wanted to try NSInputStream.
I saw from I cannot initialize a NSInputStream that the url is supposed to be local. Any idea how to stream a file from Dropbox?
Thanks.
- (void)setUpStreamForFile {
// iStream is NSInputStream instance variable already declared
iStream = [NSInputStream inputStreamWithURL:url];
[iStream setDelegate:self];
[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[iStream open];
}
Hey don't hesitate to use AFNetworking it is a good framework to manipulate your connections and download content. This is an example to download a file from an URL:
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:#"http://example.com/download.zip"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
NSLog(#"File downloaded to: %#", filePath);
}];
[downloadTask resume];
For more information you can check the official information HERE
so thanks to rmaddy's suggestion, I looked up NSURLConnection but decided to use the features of NSURLSession instead.
I used the NSURLSessionDownloadTask like this. Familiarity with the Dropbox chooser should help.
-(IBAction)didPressChooser:(id)sender {
{
[[DBChooser defaultChooser] openChooserForLinkType:DBChooserLinkTypeDirect
fromViewController:self completion:^(NSArray *results)
{
if ([results count]) {
DBChooserResult *_result = results[0];
NSString *extension = [_result.name pathExtension];
if ([extension isEqualToString:#"m4a"]) {
url = _result.link; //url has already been declared elsewhere
DBFileName = _result.name; //DPFileName has also been declared. It's a string
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate:self delegateQueue: [NSOperationQueue mainQueue]];
NSURLSessionDownloadTask *getFile = [session downloadTaskWithURL:url];
[getFile resume];
}
} else {
// User canceled the action
}
}];
}
}
Once you have that, you put in another method that works as the completion handler.
-(void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); //I put the file in a temporary folder here so it doesn't take up too much room.
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectory, DBFileName];
NSData *data = [NSData dataWithContentsOfURL:location];
[data writeToFile:filePath atomically:YES];
url = [NSURL fileURLWithPath:filePath]; //Yep, I needed to re-assign url for use elsewhere.
//do other stuff with your local file now that you have its url!
}
A bonus is that you get to keep track of the download progress with this awesome feature:
-(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);
}
Anyway, hope someone finds this useful. Works much faster on my iPhone 4 than NSURLSessionDataTask which works in a similar manner.

Resources