BGTaskScheduler Terminated due to memory issue

Hello everybody! I'm currently working on a Bluetooth Low Energy Sync that is using BGTaskScheduler & successfully running periodically in the Background on iOS 26. I did watch this years WWDC Session 227 (Finish tasks in the background) & follow the recommendations as suggested. Currently, the App is only using 37 Mb (iPhone 12 mini) & no Location or other services are running in Background.

However, when opening Safari & scrolling through some webpages, the App is killed because of "Terminated due to memory issue". I profiled the App & followed advice when it comes to reducing the memory footprint of the App. Are there any additional steps I can take to prevent the App being killed? Are there any recommendations for periodically scheduled Tasks when it comes to the Interval? Do more frequent Tasks (30min compared to one or two hours) have any impact? I tried many different schedules but none seem to make a difference. From my observation, the App is first suspended & eventually killed because of the Memory Pressure. Any hints, suggestions or recommendations are highly appreciated!

Thanks a lot for the support!

Answered by DTS Engineer in 868978022

Thanks a lot for getting back so quickly & your detailed response! The problem I'm trying to solve is to reliably sync data from a BLE peripheral in the background over a longer period of time — either during the day or the night — between our user opening the app.

There's a mix of technologies which can make this work. First, and foremost, CoreBluetooth has its own background architecture. That background architecture is extremely powerful, including support for relaunching the app into the background. That actually leads to another question, namely, what is your app actually doing when it's terminated? The system has a complex system for determining with which process it should terminate, but I'd generally expect it to avoid terminating apps that are engaged in active Bluetooth communication. More to the point, assuming your app is engaged in active Bluetooth communication, I wouldn't really expect to have even suspended, a Bluetooth exchange generally happens fast enough that the system never suspends you.

In terms of other system support, I see a few different options:

  1. If the user is initiating the transfer, then BGContinuedProcessingTask would let you extend foreground runtime for several minutes.

  2. For longer, user-initiated, transfers, you could use a Live Activity to provide custom feedback about what's going on while also extending your run time.

  3. If you specifically want the transfer to be a "background" transfer that doesn't disrupt the user, then using BGProcessingTaskRequest to "start" the transfer window might work reasonably well. The processing task itself will give "a few minutes" of run time; however, the other point is that the processing task time window is specifically chosen to minimize the possibility of the user interacting with their device AT ALL. In practice, I'd expect that to GREATLY reduce the likelihood of your app being terminated due to memory, as the device is unlikely to be generating the same kind of memory contention and "spikes" normal user usage does.

I think the biggest question here is how much time you actually need, as the more time you need to complete the work, the harder it's going to be to "guarantee" that time, particularly if the work can't be done progressively but must be completed in a single session. If you specifically need a large chunk of continuous runtime, then the best approach is to do with the user’s involvement (1 or 2).

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

However, when opening Safari & scrolling through some webpages, the App is killed because of "Terminated due to memory issue". I profiled the App & followed advice when it comes to reducing the memory footprint of the App. Are there any additional steps I can take to prevent the App being killed?

To be honest, I think you're looking at this issue the wrong way. Terminating suspended apps to free memory is part of the system’s normal architecture, not necessarily a bug or problem. Similarly, reducing your memory usage is certainly valuable and appreciated, but it isn't really possible to reduce it to the point where the system will NEVER terminate your app. That's just not how the system works.

Are there any recommendations for periodically scheduled Tasks when it comes to the Interval? Do more frequent Tasks (30min compared to one or two hours) have any impact? I tried many different schedules but none seem to make a difference.

I wouldn't expect this to make much difference.

That leads to here:

Any hints, suggestions or recommendations are highly appreciated!

My big question is what problem are you actually having? Our background APIs are generally designed so that apps can be launched into the background again "later", so it shouldn't really matter that the system terminated your app.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Dear Kevin,

thanks a lot for getting back so quickly & your detailed response! The problem I'm trying to solve is to reliable sync data from a BLE peripheral in Background over a longer period of time — either during the day or the night — between our user opening the App. However, once the app is killed, the task is no longer scheduled in the Background until the App is reopened, the Task resubmitted & re-scheduled.

So I'm looking for a way to rerun the Task even though the App was killed. Is there any way to launch the App into Background "later" as you mentioned other than having to open the App again?

Accepted Answer

Thanks a lot for getting back so quickly & your detailed response! The problem I'm trying to solve is to reliably sync data from a BLE peripheral in the background over a longer period of time — either during the day or the night — between our user opening the app.

There's a mix of technologies which can make this work. First, and foremost, CoreBluetooth has its own background architecture. That background architecture is extremely powerful, including support for relaunching the app into the background. That actually leads to another question, namely, what is your app actually doing when it's terminated? The system has a complex system for determining with which process it should terminate, but I'd generally expect it to avoid terminating apps that are engaged in active Bluetooth communication. More to the point, assuming your app is engaged in active Bluetooth communication, I wouldn't really expect to have even suspended, a Bluetooth exchange generally happens fast enough that the system never suspends you.

In terms of other system support, I see a few different options:

  1. If the user is initiating the transfer, then BGContinuedProcessingTask would let you extend foreground runtime for several minutes.

  2. For longer, user-initiated, transfers, you could use a Live Activity to provide custom feedback about what's going on while also extending your run time.

  3. If you specifically want the transfer to be a "background" transfer that doesn't disrupt the user, then using BGProcessingTaskRequest to "start" the transfer window might work reasonably well. The processing task itself will give "a few minutes" of run time; however, the other point is that the processing task time window is specifically chosen to minimize the possibility of the user interacting with their device AT ALL. In practice, I'd expect that to GREATLY reduce the likelihood of your app being terminated due to memory, as the device is unlikely to be generating the same kind of memory contention and "spikes" normal user usage does.

I think the biggest question here is how much time you actually need, as the more time you need to complete the work, the harder it's going to be to "guarantee" that time, particularly if the work can't be done progressively but must be completed in a single session. If you specifically need a large chunk of continuous runtime, then the best approach is to do with the user’s involvement (1 or 2).

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Thanks such much again for your time & effort Kevin! I did look into the Documentation & tried different approaches but none seems to work. I also found a BLE sample project on the Apple Developer Documentation & it is also terminated after some time. However, unlike the Documentation states, I wasn't able to implement State Restoration and receive Characteristic updates once the App is terminated because of Memory Pressure. But I will continue to investigate into this direction.

May I suggest to update the Documentation around Background Tasks to reflect the iOS Background Execution Limits https://developer.apple.com/forums/thread/685525 Neither the WWDC session nor the Documentation mention these limits. Additionally, several WWDC session like "Background execution demystified" & "Why is my app getting killed" are no longer available.

Thanks a lot again

Thanks so much again for your time and effort, Kevin! I did look into the documentation and tried different approaches, but none seem to work. I also found a BLE sample project on the Apple Developer Documentation, and it is also terminated after some time.

Yes, and that's expected. Across most/all our background APIs, the focus isn't on ensuring that processes never terminate but is instead on relaunching the process and returning it to normal operation.

Case in point, this is the real problem here:

However, unlike the documentation states, I wasn't able to implement State Restoration and receive Characteristic updates once the app is terminated because of Memory Pressure.

Why? Is the problem that you're not being relaunched or is state restoration failing? What happens from the accessory side? One side note here is that what triggers the relaunch is activity from the BLE accessory, so if your accessory never generates activity "back" to the iOS device, then you may not relaunch.

May I suggest updating the documentation around Background Tasks to reflect the iOS Background Execution Limits https://developer.apple.com/forums/thread/685525

Please file a bug asking for this and post the bug number back here.

Neither the WWDC session nor the documentation mentions these limits.

Yes, this has been an unfortunate consequence of how our documentation has evolved over time. Historically, the system was documented as what were basically a series of "books", each of which was then updated each year as the system evolved. Case in point, here is the Wayback Archive link to the "iOS App Programming Guide", which actually covers our basic multitasking architecture in detail:

<https://web.archive.org/web/20111223092435/http://developer.apple.com/library/IOS/#documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40007072-CH1-SW1>

FYI, part of the problem with this "book" approach is that because the guide was intended to be of a relatively "fixed" length (so that, in theory, a person could sit down and read it), the ongoing evolution of the system meant that the documentation of existing technology/architecture kept getting shorter and less detailed (to make room for new "stuff").

In any case, several years ago the decision was made to move away from this "book" approach, focusing on lower-level documentation (like the class references) and shorter, more task-focused "guides". That's had benefits but, yes, our broader "conceptual" documentation is much less complete.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

BGTaskScheduler Terminated due to memory issue
 
 
Q