NSURLSession background upload not working

对着背影说爱祢 提交于 2019-12-05 03:13:24
aman.sood

There is one thing wanted to make it clear is you cannot keep running in background any task for long, as Apple doesn't allows you. In only special cases Apple consider it. Best explained in Running background services in iOS

Now coming back to your question issue with your implementation is it will only work in background for upload task which are initiated when app was active and still task is not complete. Thats the reason for 1 in 50 attempts you see task is working in background.

Now to solve your issue you have to initiate all/bunch of upload at once so that incase if app goes in background still your app will be able upload files. This briliant tutorial explain different cases related to Background Transfer.

Also you can try out AFNetworking multipart request for upload.

NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"http://example.com/upload" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
        [formData appendPartWithFileURL:[NSURL fileURLWithPath:@"file://path/to/image.jpg"] name:@"file" fileName:@"filename.jpg" mimeType:@"image/jpeg" error:nil];
    } error:nil];

AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];

NSURLSessionUploadTask *uploadTask;
uploadTask = [manager
              uploadTaskWithStreamedRequest:request
              progress:^(NSProgress * _Nonnull uploadProgress) {
                  // This is not called back on the main queue.
                  // You are responsible for dispatching to the main queue for UI updates
                  dispatch_async(dispatch_get_main_queue(), ^{
                      //Update the progress view
                      [progressView setProgress:uploadProgress.fractionCompleted];
                  });
              }
              completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
                  if (error) {
                      NSLog(@"Error: %@", error);
                  } else {
                      NSLog(@"%@ %@", response, responseObject);
                  }
              }];

[uploadTask resume];

I have also the same concerns in one of my video app. This issue occurs after 14th Feb. I dig a lot and found this occurs after apple has changed their worldwide certificates. Please check this https://developer.apple.com/support/certificates/expiration/. Solution is to first revoked existing certificates from keychain access and then add new development/distribution certificates, new app id and provisioning profile. It will definitely work.

I think the problem is at the uploading part. You are initiating the next upload task after completing the previous one instead create all the uploading task once. In your appDelegate store the completion handler from application:handleEventsForBackgroundURLSession:completionHandler: as a property. Call the completion block in NSURLSessionDelegate URLSessionDidFinishEventsForBackgroundURLSession: delegate method of NSURLSessionDelegate. Make sure that all the upload tasks are completed before making the call. This tutorial http://www.appcoda.com/background-transfer-service-ios7/ explains well about the downloading task. May be you can apply the same rules for your upload task.

Santosh

I was into similar problem, the files were uploading in foreground. It stopped while in the background.

After having spent couple of days in reviewing the documentation and similar answers.This solution worked for me

  1. UIBackgroundTaskIdentifier is one of the important property which would be very useful. Create this in app delegate class and initialise it to UIBackgroundTaskInvalid in didFinishLaunchingWithOptions.

--

@interface AppDelegate ()

@property (atomic) UIBackgroundTaskIdentifier bgTask;
@property (nonatomic, weak) NSTimer *timer;

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.bgTask = UIBackgroundTaskInvalid;
    return YES;
}
  1. While the app is transitioning it's state from Foreground to Background or vice versa. We need to ensure to check this property in it's relevant delegate methods.

beginBackgroundTaskWithExpirationHandler is Very Crucial for this to actually start the background Task in the background.

--

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    self.bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        if (self.bgTask != UIBackgroundTaskInvalid) {
            [[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
            self.bgTask = UIBackgroundTaskInvalid;
        }
    }];

    self.timer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(startTracking) userInfo:nil repeats:YES];
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    // invalidate the timer if still running

    [self.timer invalidate];

    // end the background task if still present

    if (self.bgTask != UIBackgroundTaskInvalid) {
        [[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
        self.bgTask = UIBackgroundTaskInvalid;
    }
}

You can debug the code in real device and check it in proper network conditions.

  1. Also, you can add the below delegate method to keep track of the bytesSent.

--

- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
   didSendBodyData:(int64_t)bytesSent
    totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
    // Periodically informs the delegate of the progress of sending body content to the server.

    // Compute progress percentage
    float progress = (float)totalBytesSent / (float)totalBytesExpectedToSend;

    // Compute time executed so far
    NSDate *stopTime = [NSDate date];
    NSTimeInterval executionTime = [stopTime timeIntervalSinceDate:startTime];

    // Send info to console
    NSLog(@"%s bytesSent = %lld, totalBytesSent: %lld, totalBytesExpectedToSend: %lld, progress %.3f, time (s): %.1f", __PRETTY_FUNCTION__, bytesSent, totalBytesSent, totalBytesExpectedToSend, progress*100, executionTime);


}

I Hope it helps for someone.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!