[iOS] Unzip in URLSessionDidFinishEventsForBackgroundURLSession

Dear eskimo

I am working on application (enterprise edition) where i need to download 1,4 gb of corporate files after the installation. All should work in background. It is about 3000+ small files. I tried such approaches:
- downloading small files with help of background NSURLSession. And that gave a lot of issues. As a result i found appropriate explanations from your side here https://forums.developer.apple.com/message/42351#42351 So i decided to switch to zip download.
- achiving all files at the server side and downloading one big zip file. Download goes fine even in background BUT when i get to URLSessionDidFinishEventsForBackgroundURLSession and try to unzip iOs killes the app after 30 sec of execution time. However unzip process takes about 150 seconds on iphone 6 so this solution doesnt work.

- then i tried to ask for an extended background time in beginBackgroundTaskWithExpirationHandler when URLSessionDidFinishEventsForBackgroundURLSession is called and it gave me only 40 seconds. But this is also not enough for unzip process which takes about 150 second.
Would you be so kind to help me with some ideas here? Maybe i should ask for an extended background time later or some other trick?

Accepted Reply

Usually this will be not enough to finish extraction and app will be terminated here.

You’re missing a key point here. The system doesn’t always terminate your app when you run out of background execution time. Rather it suspends your app. At some point in the future it may terminate your suspended app to reclaim memory, but it may also resume your app, either because the user brought it to the front or for some other background activity. If it does resume your app, for whatever reason, your unzip code will start running again.

Maybe we should inform user before backgroundTimeRemaining will end in order to get him back to the application which will give background CPU time to the application?

The

backgroundTimeRemaining
property is almost never the right solution. If you want to take some sort of remedial action, you should do that in your expiry handler. You should read my UIApplication Background Task Notes post for more on this.

Is there a way to inform user that data finished loading but not fully extracted due to the limit of background taks?

You can do that via a local notification. One option is to post a local notification for some time in the future, like tomorrow. If you get enough background time to complete the unzip you can cancel the notification. If not, the notification fires and the user can bring the app to the front to complete the job.

One other thing you should consider is incremental extraction. With the approach I outlined above you can run into problems as follows:

  1. Your app stars a download.

  2. Download completes and your app starts an extraction.

  3. Your app runs out of background task time, but it leaves the extraction running in the hope it’ll finished when the app gets resumed.

  4. The system suspends your app.

  5. After some time in the background the system resumes your app.

  6. The system relaunches your app in the background for some other reason (or the user launches your app to the foreground).

The problem here is that any work you get done between steps 2 and 3 is likely to be lost. You can avoid that by doing incremental extraction. That is, when you extract a file within the zip:

  1. Write it to a temporary location.

  2. When it’s done, move it into place.

  3. Make a note on disk that you’ve done that file.

If you die halfway through dealing with the entire archive you can then resume where you left off.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"


WWDC runs Mon, 4 Jun through to Fri, 8 Jun. During that time all of DTS will be at the conference, helping folks out face-to-face.

Replies

One option here is to break the data into chunks and then download and unzip each chunk in sequence. You’ll have to strike a balance between your unzip problem and the constraints laid down by the resume rate limiter.

Another option is to treat the unzip operation as you would any other long-term CPU-bound operation that you run in the background. Your app can suspend midway through this operation without any deleterious effects, so you can complete your download run the unzip asynchronously. It will eventually complete when your app next gets background CPU time (or comes to the foreground). Or if your app gets suspended in the background and then eventually terminated, you can pick up the unzip the next time you’re launched.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"


WWDC runs Mon, 4 Jun through to Fri, 8 Jun. During that time all of DTS will be at the conference, helping folks out face-to-face.

eskimo
Thanks for your kind reply.
1) I will check max zip file size in order to be sure that extraction time is less than 30 seconds. Probably that might work for us.
orthewise we should switch to #2. But would you be so kind to clarify some questions here:
2) Based on our scenario it will be:
Download one big zip file.
Start extraction in URLSessionDidFinishEventsForBackgroundURLSession
Request extra background time with beginBackgroundTaskWithExpirationHandler when backgroundTimeRemaining time will be near 1-2 seconds. This should give us about 40 extra seconds. Which is in 30 seconds (URLSessionDidFinishEventsForBackgroundURLSession)+40 seconds (beginBackgroundTaskWithExpirationHandler) = 70 seconds in total.
Usually this will be not enough to finish extraction and app will be terminated here. So we need to process extraction after the next launch.

Is that what you are recommending?

Is there a way to inform user that data finished loading but not fully extracted due to the limit of background taks?
Maybe we should inform user before backgroundTimeRemaining will end in order to get him back to the application which will give background CPU time to the application? Because otherwise it will be terminated after some time in suspeded state.

Usually this will be not enough to finish extraction and app will be terminated here.

You’re missing a key point here. The system doesn’t always terminate your app when you run out of background execution time. Rather it suspends your app. At some point in the future it may terminate your suspended app to reclaim memory, but it may also resume your app, either because the user brought it to the front or for some other background activity. If it does resume your app, for whatever reason, your unzip code will start running again.

Maybe we should inform user before backgroundTimeRemaining will end in order to get him back to the application which will give background CPU time to the application?

The

backgroundTimeRemaining
property is almost never the right solution. If you want to take some sort of remedial action, you should do that in your expiry handler. You should read my UIApplication Background Task Notes post for more on this.

Is there a way to inform user that data finished loading but not fully extracted due to the limit of background taks?

You can do that via a local notification. One option is to post a local notification for some time in the future, like tomorrow. If you get enough background time to complete the unzip you can cancel the notification. If not, the notification fires and the user can bring the app to the front to complete the job.

One other thing you should consider is incremental extraction. With the approach I outlined above you can run into problems as follows:

  1. Your app stars a download.

  2. Download completes and your app starts an extraction.

  3. Your app runs out of background task time, but it leaves the extraction running in the hope it’ll finished when the app gets resumed.

  4. The system suspends your app.

  5. After some time in the background the system resumes your app.

  6. The system relaunches your app in the background for some other reason (or the user launches your app to the foreground).

The problem here is that any work you get done between steps 2 and 3 is likely to be lost. You can avoid that by doing incremental extraction. That is, when you extract a file within the zip:

  1. Write it to a temporary location.

  2. When it’s done, move it into place.

  3. Make a note on disk that you’ve done that file.

If you die halfway through dealing with the entire archive you can then resume where you left off.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"


WWDC runs Mon, 4 Jun through to Fri, 8 Jun. During that time all of DTS will be at the conference, helping folks out face-to-face.

@eskimo Thank you, that really helped me. It is really cool that you provided this info because not all is described clearly in documentaion and also i saw a lot of posts at stackowerflow with similar problems.