BGProcessingTask File Upload Limits

I have BGProcessingTask & BGAppRefreshTask working fine. The main purpose of my use of BGProcessingTask is to upload a file to AWS S3 using multipart/form-data. I have found that any file above about 2.5MB times out after running almost four minutes. If I run the same RESTful api using curl or Postman, I can upload a 25MB file in 3 seconds or less.

I have tried to deliberately set .earliestBeginDate to 01:00 or 02:00 local time on the iPhone, but that does not seem to help.

I use the delegate (yes, I am writing in Objective C) - URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend: and find that the iOS system uploads about 140kB every 15 seconds or so.

I am looking for recommendations or insight into how I might enable uploads of 25MB files. I would be happy it I could do just one a day for my use case.

I provide code on how I set up the NSURLSession and NSURLSessionDownloadTask, as it is my guess that if there is something that needs to be modified it is there.

I have to believe there is a solution for this since I read in many posts here and in StackOverflow how developers are using this functionality for uploading many, many files.

NSURLSessionConfiguration *sConf = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:bkto.taskIdentifier];
sConf.URLCache = [NSURLCache sharedURLCache];
sConf.waitsForConnectivity = YES;
sConf.allowsCellularAccess = NO;
sConf.networkServiceType = NSURLNetworkServiceTypeVideot;
sConf.multipathServiceType = NSURLSessionMultipathServiceTypeNone;
sConf.discretionary = YES;
sConf.timeoutIntervalForResource = kONEHOURINTERVAL;
sConf.timeoutIntervalForRequest = kONEMINUTEINTERVAL;
sConf.allowsExpensiveNetworkAccess = NO ;
sConf.allowsConstrainedNetworkAccess = NO;
sConf.sessionSendsLaunchEvents = YES;
myURLSession = [NSURLSession sessionWithConfiguration:sConf delegate:self delegateQueue:nil];

And then later in the code...

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:pth]];
request.HTTPMethod = kHTTPPOST;
request.HTTPBody = [NSData my body data];
request.timeoutInterval = 60;
[request setValue:@"*/*" forHTTPHeaderField:@"Accept"];
[request setValue:@"en-us,en" forHTTPHeaderField:@"Accept-Language"];
[request setValue:@"gzip, deflate, br" forHTTPHeaderField:@"Accept-Encoding"];
[request setValue:@"ISO-8859-1,utf-8" forHTTPHeaderField:@"Accept-Charset"];
[request setValue:@"600" forHTTPHeaderField:@"Keep-Alive"];
[request setValue:@"keep-alive" forHTTPHeaderField:@"Connection"];
NSString *contType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",bnd];
[request setValue:contType forHTTPHeaderField:@"Content-Type"];
[request addValue:[NSString stringWithFormat:@"%lu",(unsigned long)myData.length] forHTTPHeaderField:@"Content-Length"];

and here are a few lines from my logs to show the infrequent multi-part uploads of only small chunks of data by the iOS system:

-[BKSessionManager URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:]: bytesSent = 393,216
-[BKSessionManager URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:]: totalBytesSent = 393,216
-[BKSessionManager URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:]: task = BackgroundDownloadTask <76A81A80-4703-4686-8742-A0048EB65108>.<2>, time Fri Mar 7 16:25:27 2025
-[BKSessionManager URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:]: bytesSent = 131,072
-[BKSessionManager URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:]: totalBytesSent = 524,288
-[BKSessionManager URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:]: task = BackgroundDownloadTask <76A81A80-4703-4686-8742-A0048EB65108>.<2>, time Fri Mar 7 16:25:42 2025
-[BKSessionManager URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:]: bytesSent = 131,072
-[BKSessionManager URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:]: totalBytesSent = 655,360
-[BKSessionManager URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:]: task = BackgroundDownloadTask <76A81A80-4703-4686-8742-A0048EB65108>.<2>, time Fri Mar 7 16:25:56 2025
-[BKSessionManager URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:]: bytesSent = 131,072
-[BKSessionManager URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:]: totalBytesSent = 786,432
Answered by DTS Engineer in 830495022

I am looking for recommendations or insight into how I might enable uploads of 25MB files. I would be happy it I could do just one a day for my use case.

I think the issue here is that you're using the background session (and discretionary=YES) and you should just use the default. You're basically telling the system "I don't really need this right now" and that's exactly what it's doing. The background session is useful when you want want to schedule/defer larger transfers, but for small transfers like this it's creating more problems than it solves.

Note that my "rule of thumb" is that timeoutIntervalForResource should generally be in "days+", never minutes. That's particularly true in BGProcessingTask, since the background session is effectively deffering downloads into the same execution window you're running in.

Quick side comment on this point:

I have tried to deliberately set .earliestBeginDate to 01:00 or 02:00 local time on the iPhone, but that does not seem to help.

It's important to understand that you have very little control over when a background task actually runs. You can tell the system when NOT to run the task, but you CANNOT control when the task is actually going to run. Putting that in concrete terms, the system is using heuristics to determine the best time for tasks to run and that's when tasks are GOING to run. You can use earliestBeginDate to defer work "past" that point, but you're not going to change when the work actually runs.

Note that this means you can create configurations that will NEVER fire. For example, if:

  1. Your app is used by the user every day.

  2. Every time your app runs, reschedules it's BGProcessingTask's to run an 6am the next day.

  3. The device's task "window" is from 2am -> 3am (pretty common).

...then you BGProcessingTask will basically never run. Take a look at this post for a rundown of the issues many developers run into this background tasks.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Accepted Answer

I am looking for recommendations or insight into how I might enable uploads of 25MB files. I would be happy it I could do just one a day for my use case.

I think the issue here is that you're using the background session (and discretionary=YES) and you should just use the default. You're basically telling the system "I don't really need this right now" and that's exactly what it's doing. The background session is useful when you want want to schedule/defer larger transfers, but for small transfers like this it's creating more problems than it solves.

Note that my "rule of thumb" is that timeoutIntervalForResource should generally be in "days+", never minutes. That's particularly true in BGProcessingTask, since the background session is effectively deffering downloads into the same execution window you're running in.

Quick side comment on this point:

I have tried to deliberately set .earliestBeginDate to 01:00 or 02:00 local time on the iPhone, but that does not seem to help.

It's important to understand that you have very little control over when a background task actually runs. You can tell the system when NOT to run the task, but you CANNOT control when the task is actually going to run. Putting that in concrete terms, the system is using heuristics to determine the best time for tasks to run and that's when tasks are GOING to run. You can use earliestBeginDate to defer work "past" that point, but you're not going to change when the work actually runs.

Note that this means you can create configurations that will NEVER fire. For example, if:

  1. Your app is used by the user every day.

  2. Every time your app runs, reschedules it's BGProcessingTask's to run an 6am the next day.

  3. The device's task "window" is from 2am -> 3am (pretty common).

...then you BGProcessingTask will basically never run. Take a look at this post for a rundown of the issues many developers run into this background tasks.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Kevin Elliott, This is very helpful, thank you. I have found that during testing using Background Sessions using a NSURLSessionUploadTask rather than NSURLSessionDownloadTask for a file upload increased the speed of the file upload significantly. I also found that following the pace of the file upload can be deceiving as the first 3-5MB can take a few minutes, but then each subsequent part uploads much faster. So if uploadTaskWithRequest: fromFile: will work on an NSURLSession executed through the BGProcessingTask Framework I have a great solution for my problem. That is what I am testing now.. Your insight should help.

Written by DTS Engineer in 830495022
I think the issue here is that you're using the background session (and discretionary=YES) and you should just use the default. You're basically telling the system "I don't really need this right now" and that's exactly what it's doing. The background session is useful when you want want to schedule/defer larger transfers, but for small transfers like this it's creating more problems than it solves.

Kevin, I reread your post and it prompts a couple of questions.

  1. Don't use a background session

Do I understand correctly that you recommended I NOT use a background session for a BGProcessingTask? Does this apply also to BGAppRefreshTask as well?

  1. .earliestBeginDate

Are you recommending that I disregard trying to set this for some off hour as the system will do that anyway?

  1. Rescheduling

If the BGTask does not complete, should I reschedule? Or will the system automatically reschedule if setTaskCompletedWithSuccess: is set to NO?

You have been most helpful, thank you!!

[quote='830495022, DTS Engineer, /thread/777033?answerId=830495022#830495022']

Elliott

Written by DTS Engineer in 830495022
Elliott

You can disregard answering my first question. I coded it and it works without background session. So thank you for pointing that out. But if you can take a look at my other two questions - .earliestBeginDate & Rescheduling - it would be appreciated.

Do I understand correctly that you recommended I NOT use a background session for a BGProcessingTask?

This depends on what you're trying to do. More specifically, you should use the background session for large and/or lower priority transfers and the standard session for transfers you want done "now".

Does this apply also to BGAppRefreshTask as well?

Yes, with the qualifier that having less execution time also changes the transfer size you can "reasonably" expect to complete.

Are you recommending that I disregard trying to set this for some off hour as the system will do that anyway?

Yes. More specifically, the timing here is about when your app needs/wants the data, NOT when you think the system "ought" to run it. Trying to "game" the timing like this just ends up introducing odd edge cases and failures that wouldn't otherwise exist.

As a concrete example, lets say you have some maintenance work you want to run "soon". That is, you don't actually care when it's actually "done", you just want it happen regularly. If you:

  1. Schedule it to fire at 2am
  2. Your app is used on a daily basis
  3. That user is a night shift worker
  4. They use the app at 9am on Monday, then go to bed.

Best case, you schedule the work on Monday morning and "leave" it alone:

-> Your task fires on Tuesday while they're sleeping (say "3pm").

Worst case, you schedule the work again on Tuesday (since it hasn't run):

-> Your task never fires, since you keep rescheduling it after the time it would have run.

Alternatively, you could have just said "now" (since you didn't actually care when it ran), in which case it would have fired on Monday at ~3pm

Or will the system automatically reschedule if setTaskCompletedWithSuccess: is set to NO?

The system will take care of it.

If the BGTask does not complete, should I reschedule?

This doesn't matter for BGAppRefreshTask, as their scheduling is almost entirely driven by direct app usage.

For BGProcessingTask, the answer is no and yes. More specifically:

  • No-> Scheduling is partly driven by page, so "active" rescheduling ends up reducing your tasks priority.

  • Yes-> "Old enough" task are eventually (silently) discarded, so after "enough" time your task is never going to fire.

For both cases, my practical recommendation is that you do what our sample does, which is reschedule BGProcessingTask's which haven't fired after ~7 days and schedule BGRefreshTask at launch and (as I recall) whenever they fire.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

BGProcessingTask File Upload Limits
 
 
Q