Hi,
We are running into issues with iOS app prewarming, where the system launches our app before the user has entered their passcode.
In our case, the app stores flags, counters, and session data in UserDefaults and the Keychain. During prewarm launches:
UserDefaults only returns default values (nil, 0, false). We have no way of knowing whether this information is valid or just a placeholder caused by prewarming.
Keychain items with kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly are inaccessible, which can lead to broken business logic (the app can assume no session exists).
No special launch options or environment variables appear to be set.
We can reproduce this 100% of the time by starting a Live Activity in the app before reboot.
Here’s an example of the workaround we tried, following older recommendations:
__attribute__((constructor))
static void ModuleInitializer(void) {
char* isPrewarm = getenv("ActivePrewarm");
if (isPrewarm != NULL && isPrewarm[0] == '1') {
exit(0); // prevent prewarm launch from proceeding
}
}
On iOS 16+, the ActivePrewarm environment variable doesn’t seem to exist anymore (though older docs and SDKs such as Sentry reference it).
We also tried listening for UIApplication.protectedDataDidBecomeAvailableNotification, but this is not specific to prewarming (it also fires when the device gets unlocked) and can cause watchdog termination if we delay work too long.
Questions:
Is there a supported way to opt out of app prewarming?
What is the correct way to detect when an app is being prewarmed?
Is the ActivePrewarm environment variable still supported in iOS 16+?
Ideally, the UserDefaults API itself should indicate whether it is returning valid stored values or defaults due to the app being launched in a prewarm session.
We understand opting out may impact performance, but data security and integrity are our priority. Any guidance would be greatly appreciated.
Processes & Concurrency
RSS for tagDiscover how the operating system manages multiple applications and processes simultaneously, ensuring smooth multitasking performance.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Hi everyone, I’m Jaswanth. My friends and I are students working on a project where we’ve developed a website and a companion app.
Here’s the key functionality:
When two users enter a virtual room, one of them is prompted to download a desktop app.
The app is built with Python and uses psutil to check for certain running processes.
It does not send any data over the internet.
It has a GUI that clearly shows the system is being monitored , it’s not hidden or running in the background silently.
We want to sign and notarize the app to make sure it runs on macOS without warning users. However, we’re concerned that since the app accesses system process information, it might be flagged as malicious.
Before we pay for the Apple Developer Program, we wanted to ask:
Will an app like this (which only reads running processes and does not exfiltrate or hide activity) be eligible for notarization?
Thanks in advance for any insights. We'd appreciate any clarity before moving forward.
Best,
Jaswanth
我在开发 Mac应用完成 后 通过Xcode 上传二进制文件的过程中, 出现了错误, 错误提示: App里面用到的 com.apple.security.application-groups 权限里面 有 group.*** 和 开发者组ID.*** 导致校验失败, 当我单独使用 group.xxx的时候, 我的程序会崩溃 , 因为里面用到了 MachPortRende 进程间通信问题, 这里默认了 开发者组ID.*** 这个路径, 错误详情: 在尝试启动 QuickFox 应用时,程序因权限问题而崩溃。具体的错误信息 bootstrap_check_in 组ID.xxxx.MachPortRendezvousServer.82392: Permission denied (1100) 显示,应用在尝试使用 Mach 端口进行进程间通信时,没有获得足够的权限, 因此 我需要您们的帮助, 如果单独用开发者组ID.*** 我们又没有权限 将数据写入 组ID.xxx里面的文件
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
Entitlements
Inter-process communication
Are there any plans to add RBI support (the sending keyword) to the OSAllocatedLock interface? So it could be used with non-sendable objects without surrendering to the unchecked API
Hello, I am programming a CLI tool to partition USB disks. I am calling diskutil to do the work, but I am hitting issues with permissions, it seems.
Here is a trial run of the same command running diskutil directly on the terminal vs running from my code:
Calling diskutil directly (works as expected)
% /usr/sbin/diskutil partitionDisk /dev/disk2 MBR Free\ Space gap 2048S fat32 f-fix 100353S Free\ Space tail 0
Started partitioning on disk2
Unmounting disk
Creating the partition map
Waiting for partitions to activate
Formatting disk2s1 as MS-DOS (FAT32) with name f-fix
512 bytes per physical sector
/dev/rdisk2s1: 98784 sectors in 98784 FAT32 clusters (512 bytes/cluster)
bps=512 spc=1 res=32 nft=2 mid=0xf8 spt=32 hds=16 hid=2079 drv=0x80 bsec=100360 bspf=772 rdcl=2 infs=1 bkbs=6
Mounting disk
Finished partitioning on disk2
/dev/disk2 (disk image):
#: TYPE NAME SIZE IDENTIFIER
0: FDisk_partition_scheme +104.9 MB disk2
1: DOS_FAT_32 F-FIX 51.4 MB disk2s1
Calling diskutil programmatically (error -69877)
% sudo ./f-fix
DEBUG: /usr/sbin/diskutil partitionDisk /dev/disk2 MBR Free Space gap 2048S fat32 f-fix 100353S Free Space tail 0
Started partitioning on disk2
Unmounting disk
Error: -69877: Couldn't open device
(Is a disk in use by a storage system such as AppleRAID, CoreStorage, or APFS?)
Failed to fix drive `/dev/disk2'
Source Code
The relevant code from my program is this:
char *args[16]; int n = 0;
args[n++] = "/usr/sbin/diskutil";
args[n++] = "partitionDisk";
args[n++] = (char *)disk;
args[n++] = (char *)scheme;
(...)
args[n++] = NULL;
char **parent_env = *_NSGetEnviron();
if (posix_spawnp(&pid, args[0], NULL, NULL, args, parent_env) != 0)
return 1;
if (waitpid(pid, &status, 0) < 0)
return 1;
return 0;
Question
Are there any system protections against running it like so? What could I be missing? Is this a Disk Arbitration issue?
Hello everyone,
I’m a new developer still learning as I go. I’m building a simple watchOS app that tracks Apple Watch battery consumption, records hourly usage data, and uses that information to predict battery life in hours.
I’ve run into an issue where background refresh completely stalls after charging and never recovers, regardless of what I do. The only way to restore normal behavior is to restart the watch.
Background refresh can work fine for days, but if the watch is charging and a scheduled background refresh tries to run during that period, it appears to be deferred—and then remains in that deferred state indefinitely. Even reopening the app or scheduling new refreshes doesn’t recover it.
Has anyone else encountered this behavior? Is there a reliable workaround?
I’ve seen a few reports suggesting that there may be a regression in scheduleBackgroundRefresh() on watchOS 26, where tasks are never delivered after certain states.
Any insights or confirmations would be greatly appreciated. Thank you!
First, our app communicates with our blood glucose monitor (CGM) using Bluetooth Low Energy (BLE).
On an iPhone 14 Pro with iOS 26.0.1, Bluetooth communication works properly even when the app is in the background and locked. Even if the phone and CGM are disconnected, the app continues to scan in the background and reconnects when the phone and CGM are back in close proximity. It won't be dormant in the background or when the screen is locked. This effectively ensures that diabetic users can monitor their blood glucose levels in real time.
However, after using iOS 26.0.1 on the iPhone 17, we've received user feedback about frequent disconnections in the background. Our logs indicate that Bluetooth communication is easily disconnected when switching to the background, and then easily dormant by the system, especially when the user's screen is locked. This situation significantly impacts users' blood glucose monitoring, and users are unacceptable. What can be done?
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
IOBluetooth
Background Tasks
Core Bluetooth
When I search, it's always people trying to do stuff in the background. I want my app to only do stuff when it is active. And this post https://developer.apple.com/forums/thread/685525 seems to have prevented replies from the start. Which means it's just a documentation page and does not belong in the discussion forums at all, because it prevents all discussion.
How to execute code on main app when interacted with a live activity, given that they are already interactable.
is there a way without opening the app?
what are the best ways?
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
Extensions
WidgetKit
ActivityKit
I have implemented a XPC server using C APIs. I want to write unit tests for it.
I came across the following links that use Swift APIs-
Testing and Debugging XPC Code With an Anonymous Listener
TN3113
I have tried to write anonymous listener code and the client code in the same file, using C APIs-
#include <unistd.h>
#include <syslog.h>
#include <pthread.h>
#include <stdio.h>
#include <xpc/xpc.h>
#include <xpc/connection.h>
#include <CoreFoundation/CoreFoundation.h>
static void Anon_Client_Connection_Handler(xpc_connection_t connection, xpc_object_t clientMessage)
{
const char *description = xpc_copy_description(clientMessage);
printf("Event received - %s\n", description);
free((void *)description);
xpc_type_t type = xpc_get_type(clientMessage);
if (type == XPC_TYPE_ERROR)
{
if (clientMessage == XPC_ERROR_CONNECTION_INVALID)
printf("Client_Connection_Handler received invalid connection n");
else if (clientMessage == XPC_ERROR_TERMINATION_IMMINENT)
printf("Client_Connection_Handler received termination notice n");
}
else
{
const char *clientMsg = xpc_dictionary_get_string(clientMessage, "message");
printf("Received from client: %s ", clientMsg);
}
}
static void Anon_Listener_Connection_Handler(xpc_connection_t connection)
{
printf("Anon_Listener_Connection_Handler called, setting up event handler \n");
xpc_connection_set_event_handler(connection, ^(xpc_object_t clientMessage) {
printf("Processing the connection! \n");
Anon_Client_Connection_Handler(connection, clientMessage);
});
xpc_connection_resume(connection);
}
int main(int argc, const char *argv[])
{
xpc_connection_t anon_listener = xpc_connection_create(NULL, NULL);
xpc_connection_set_event_handler(anon_listener, ^(xpc_object_t clientConnection) {
printf("Client tried to connect \n");
Anon_Listener_Connection_Handler(clientConnection);
});
xpc_connection_resume(anon_listener);
printf("\nINFO Anonymous connection resumed");
xpc_object_t anon_endpoint = xpc_endpoint_create(anon_listener);
xpc_connection_t clientConnection = xpc_connection_create_from_endpoint(anon_endpoint);
xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_string(message, "message", "client's message");
xpc_connection_send_message_with_reply(clientConnection, message, dispatch_get_main_queue(), ^(xpc_object_t event) {
printf("\nINFO inside reply");
const char *description = xpc_copy_description(event);
printf("\nINFO %s",description);
free((void *)description);
});
xpc_release(message);
xpc_release(anon_listener);
printf("\nINFO Releasing listener");
xpc_release(anon_endpoint);
printf("\nINFO Releasing endpoint");
// dispatch_main();
return 0;
}
and this is the output I get
INFO Anonymous connection resumed
INFO Releasing listener
INFO Releasing endpoint
I am not able to connect to the client and exchange messages. Where am I going wrong?
i am trying to create a daemon with xpc for my app by referring to https://github.com/alienator88/HelperToolApp but i keep getting XPC remote proxy error: Couldn’t communicate with a helper application. All the identifiers all correct but the helper code is not reached.
Hello everyone!
I'm having a problem with background tasks running in the foreground.
When a user enters the app, a background task is triggered. I've written some code to check if the app is in the foreground and to prevent the task from running, but it doesn't always work. Sometimes the task runs in the background as expected, but other times it runs in the foreground, as I mentioned earlier.
Could it be that I'm doing something wrong? Any suggestions would be appreciated.
here is code:
class BackgroundTaskService {
@Environment(\.scenePhase) var scenePhase
static let shared = BackgroundTaskService()
private init() {}
// MARK: - create task
func createCheckTask() {
let identifier = TaskIdentifier.check
BGTaskScheduler.shared.getPendingTaskRequests { requests in
if requests.contains(where: { $0.identifier == identifier.rawValue }) {
return
}
self.createByInterval(identifier: identifier.rawValue, interval: identifier.interval)
}
}
private func createByInterval(identifier: String, interval: TimeInterval) {
let request = BGProcessingTaskRequest(identifier: identifier)
request.earliestBeginDate = Date(timeIntervalSinceNow: interval)
scheduleTask(request: request)
}
// MARK: submit task
private func scheduleTask(request: BGProcessingTaskRequest) {
do {
try BGTaskScheduler.shared.submit(request)
} catch {
// some actions with error
}
}
// MARK: background actions
func checkTask(task: BGProcessingTask) {
let today = Calendar.current.startOfDay(for: Date())
let lastExecutionDate = UserDefaults.standard.object(forKey: "lastCheckExecutionDate") as? Date ?? Date.distantPast
let notRunnedToday = !Calendar.current.isDate(today, inSameDayAs: lastExecutionDate)
guard notRunnedToday else {
task.setTaskCompleted(success: true)
createCheckTask()
return
}
if scenePhase == .background {
TaskActionStore.shared.getAction(for: task.identifier)?()
}
task.setTaskCompleted(success: true)
UserDefaults.standard.set(today, forKey: "lastCheckExecutionDate")
createCheckTask()
}
}
And in AppDelegate:
BGTaskScheduler.shared.register(forTaskWithIdentifier: "check", using: nil) { task in
guard let task = task as? BGProcessingTask else { return }
BackgroundTaskService.shared.checkNodeTask(task: task)
}
BackgroundTaskService.shared.createCheckTask()
Issue:
Background downloads using the flutter_downloader package work perfectly in debug mode and release mode when run directly from Xcode (plugged in).
However, when I create an archive build and install the app separately (via TestFlight or direct IPA install), the background download stops working as soon as the app is minimized.
✅ What I’ve already done
Info.plist
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
<string>fetch</string>
<string>processing</string>
<string>audio</string>
<string>push-to-talk</string>
</array>
AppDelegate.swift
import UIKit
import Flutter
import Firebase
import flutter_downloader
import BackgroundTasks
@main
@objc class AppDelegate: FlutterAppDelegate {
static let backgroundChannel = "com.example.app/background_service"
private var backgroundCompletionHandler: (() -> Void)?
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
FirebaseApp.configure()
GeneratedPluginRegistrant.register(with: self)
FlutterDownloaderPlugin.setPluginRegistrantCallback(registerPlugins)
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self
}
if #available(iOS 13.0, *) {
registerBackgroundTask()
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
@available(iOS 13.0, *)
private func registerBackgroundTask() {
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.example.app.process_download_queue",
using: nil
) { [weak self] task in
guard let self = self else { return }
self.handleDownloadQueueTask(task: task as! BGProcessingTask)
}
}
@available(iOS 13.0, *)
private func handleDownloadQueueTask(task: BGProcessingTask) {
scheduleNextDownloadTask()
let headlessEngine = FlutterEngine(name: "BackgroundTaskEngine", project: nil, allowHeadlessExecution: true)
headlessEngine.run()
let channel = FlutterMethodChannel(
name: AppDelegate.backgroundChannel,
binaryMessenger: headlessEngine.binaryMessenger
)
task.expirationHandler = {
channel.invokeMethod("backgroundTaskExpired", arguments: nil)
}
channel.invokeMethod("processNextInBackground", arguments: nil) { result in
task.setTaskCompleted(success: (result as? Bool) ?? false)
}
}
override func application(
_ application: UIApplication,
handleEventsForBackgroundURLSession identifier: String,
completionHandler: @escaping () -> Void
) {
self.backgroundCompletionHandler = completionHandler
super.application(application, handleEventsForBackgroundURLSession: identifier, completionHandler: completionHandler)
}
override func applicationDidEnterBackground(_ application: UIApplication) {
if #available(iOS 13.0, *) {
scheduleNextDownloadTask()
}
}
@available(iOS 10.0, *)
override func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
if #available(iOS 14.0, *) {
completionHandler([.list, .banner, .badge, .sound])
} else {
completionHandler([.alert, .badge, .sound])
}
}
@available(iOS 10.0, *)
override func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
completionHandler()
}
}
// MARK: - Helper
@available(iOS 13.0, *)
func scheduleNextDownloadTask() {
let request = BGProcessingTaskRequest(identifier: "com.example.app.process_download_queue")
request.requiresNetworkConnectivity = true
request.requiresExternalPower = false
request.earliestBeginDate = Date(timeIntervalSinceNow: 60)
do {
try BGTaskScheduler.shared.submit(request)
print("BGTask: Download queue processing task scheduled successfully.")
} catch {
print("BGTask: Could not schedule download queue task: \(error)")
}
}
private func registerPlugins(registry: FlutterPluginRegistry) {
if !registry.hasPlugin("FlutterDownloaderPlugin") {
FlutterDownloaderPlugin.register(with: registry.registrar(forPlugin: "FlutterDownloaderPlugin")!)
}
}
🧩 Observations
Background download works correctly when:
The app is plugged in and run via Xcode (release/debug)
It stops working when:
The app is installed from an archived build (IPA/TestFlight) and minimized
All entitlements and background modes are properly added.
Provisioning profile includes required background modes.
❓Question
Is there any known limitation or signing difference between Xcode run and archived release builds that could cause URLSession background tasks not to trigger?
Has anyone faced a similar issue when using flutter_downloader on iOS 13+ with BGTaskScheduler or URLSession background configuration?
Any help or working setup example for production/TestFlight would be appreciated.
If I create a BGContinuedProcessingTaskRequest, register it, and then "do work" within it appropriately reporting progress, and before my task has finished doing all the work it had to do, its expirationHandler triggers...
does the task later try again?
Or does it lose the execution opportunity until the app is next re-launched to the foreground?
In my testing, I never saw my task execute again once expired (which suggests the latter?).
I was able to easily force this expiry by starting my task, backgrounding my app, then launching the iOS Camera App. My example is just using test code inspired from https://developer.apple.com/documentation/backgroundtasks/performing-long-running-tasks-on-ios-and-ipados
let request = BGContinuedProcessingTaskRequest(identifier: taskIdentifier, title: "Video Upload", subtitle: "Starting Upload")
request.strategy = .queue
BGTaskScheduler.shared.register(forTaskWithIdentifier: taskIdentifier, using: nil) { task in
guard let task = task as? BGContinuedProcessingTask else { return }
print("i am a good task")
var wasExpired = false
task.expirationHandler = {
wasExpired = true
}
let progress = task.progress
progress.totalUnitCount = 100
while !progress.isFinished && !wasExpired {
progress.completedUnitCount += 1
let formattedProgress = String(format: "%.2f", progress.fractionCompleted * 100)
task.updateTitle(task.title, subtitle: "Completed \(formattedProgress)%")
sleep(1)
}
if progress.isFinished {
print ("i was a good task")
task.setTaskCompleted(success: true)
} else {
print("i was not a good task")
task.setTaskCompleted(success: false)
}
}
try? BGTaskScheduler.shared.submit(request)
Apologies if this is clearly stated somewhere and I'm missing it.
I am currently developing a macOS app that can show system HUDs in the Notch
Till Sequoia I used to kill the OSDUIHelper process (which displays the default macOS Volume and Brightness control HUDs) - and replaced it with my app's HUDs
But, it is not working on macOS Tahoe anymore as the OSDUIHelper process is no longer there due to the UI changes
Has the process been renamed - or is there any other way to kill the process?
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
Swift
macOS
SwiftUI
Background Tasks
How can you force cancel a task that doesn't need cleanup and doesn't check for cancellation?
If this cannot be done, would this be a useful addition to Swift?
Here is the situation:
The async method doesn't check for cancellation since it is not doing anything repetively (for example in a loop). For example, the method may be doing "try JSONDecoder().decode(Dictionary<String, ...>.self, from: data)" where data is a large amount.
The method doesn't need cleanup.
I would like the force cancellation to throw an error. I am already handling errors for the async method.
My intended situation if that the user request the async method to get some JSON encoded data, but since it is taking longer that they are willing to wait, they would tap a cancellation button that the app provides.
Will App be terminated or suspended when updating to incremental version from app store?
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
App Store
Core Location
Background Tasks
Just trying to understand the documentation.
Obviously, we can send a request to the service to return all the data at once. Can the data arrive in pieces, involving either multiple async callbacks or a Combine Publisher?
In the header for workloop.h there is this note:
A dispatch workloop is a "subclass" of dispatch_queue_t which can be passed to all APIs accepting a dispatch queue, except for functions from the dispatch_sync() family. dispatch_async_and_wait() must be used for workloop objects. Functions from the dispatch_sync() family on queues targeting a workloop are still permitted but discouraged for performance reasons.
I have a couple questions related to this. First, I'd like to better understand what the alluded-to 'performance reasons' are that cause this pattern to be discouraged in the 'queues targeting a workloop' scenario. From further interrogation of the headers, I've found these explicit callouts regarding differences in the dispatch_sync and dispatch_async_and_wait API:
dispatch_sync:
Work items submitted to a queue with dispatch_sync() do not observe certain queue attributes of that queue when invoked (such as autorelease frequency and QOS class).
dispatch_async_and_wait:
Work items submitted to a queue with dispatch_async_and_wait() observe all queue attributes of that queue when invoked (inluding [sic] autorelease frequency or QOS class).
Additionally, dispatch_async_and_wait has a section of the headers devoted to 'Differences with dispatch_sync()', though I can't say I entirely follow the distinctions it attempts to draw.
Based on that, my best guess is that the 'performance reasons' are something about either QoS not being properly respected/observed or some thread context switching differences that can degrade performance, but I would appreciate insight from someone with more domain knowledge.
My second question is a bit more general – taking a step back, why exactly do these two API exist? It's not clear to me from the existing documentation I've found why I would/should prefer dispatch_sync over dispatch_async_and_wait (other than the aforementioned callout noting the former is unsupported on workloops). What is the motivation for preserving both these API vs deprecating dispatch_sync in favor of dispatch_async_and_wait (or functionally subsuming one with the other)?
Credit to Luna for originally posing/inspiring these questions.
iOS BGProcessingTask + Background Upload Not Executing Reliably on TestFlight (Works in Debug)
Description:
We are facing an issue with BGTaskScheduler and BGProcessingTask when trying to perform a background audio-upload flow on iOS. The behavior is inconsistent between Debug builds and TestFlight (Release) builds.
Summary of the Problem
Our application records long audio files (up to 1 hour) and triggers a background upload using:
BGTaskScheduler
BGProcessingTaskRequest
Background URLSession (background with identifier)
URLSession background upload task + AppDelegate.handleEventsForBackgroundURLSession
In Debug mode (Xcode → Run on device), everything works as expected:
BGProcessingTask executes
handleEventsForBackgroundURLSession fires
Background URLSession continues uploads reliably
Long audio files successfully upload even when the app is in background or terminated
However, in TestFlight / Release mode, the system does not reliably launch the BGProcessingTask or Background URLSession events.
Technical Details
We explicitly register BGTaskScheduler:
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "example.background.process",
using: nil
) { task in
self.handleBackgroundProcessing(task: task as! BGProcessingTask)
}
We schedule it using:
let request = BGProcessingTaskRequest(identifier: "example.background.process")
request.requiresNetworkConnectivity = true
request.requiresExternalPower = false
try BGTaskScheduler.shared.submit(request)
We also use Background URLSession:
let config = URLSessionConfiguration.background(withIdentifier: sessionId)
config.sessionSendsLaunchEvents = true
config.isDiscretionary = false
AppDelegate.handleEventsForBackgroundURLSession is implemented correctly and works in Debug.
Issue Observed (TestFlight Only)
In TestFlight builds:
BGProcessingTask rarely triggers, or the system marks it as NO LONGER RUNNING.
Background upload tasks sometimes never start or complete.
No logs appear from our BGProcessingTask handler.
system logs show messages like:
NO LONGER RUNNING bgProcessing-example.background.process
Tasks running in group [com.apple.dasd.defaultNetwork] are 1!
This occurs most frequently for large audio uploads (30–60 minutes), while small files behave normally.
What We Have Verified
Proper Info.plist values:
Permitted background modes: processing, audio, fetch
BGTaskSchedulerPermittedIdentifiers contains our identifier
BGProcessingTask is being submitted successfully (no errors)
App has microphone permission + background audio works
Device plugged/unplugged doesn’t change outcome
Key Question for Apple
We need clarification on:
Why BGProcessingTask behave differently between Debug and TestFlight builds?
Are there additional restrictions or heuristics (related to file size, CPU usage, runtime, network load, or power constraints) that cause BGProcessingTask to be throttled or skipped in Release/TestFlight?
How can we guarantee a background upload continues reliably for large files (100MB–500MB) on TestFlight and App Store builds?
Is there an Apple-recommended pattern to combine BGProcessingTask + Background URLSession for long-running uploads?
Expected Result
Background uploads should continue reliably for long audio files (>30 minutes) when the app goes to background or is terminated, in the same way they currently function in Debug builds.
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
iOS
Background Tasks
Foundation
CFNetwork