I have been experimenting with the BGContinuedProcessingTask API recently (and published sample code for it https://github.com/infinitepower18/BGContinuedProcessingTaskDemo)
I have noticed that if I lock the phone, the code that runs as part of the task stops executing. My sample code simply updates the progress each second until it gets to 100, so it should be completed in 1 minute 40 seconds. However, after locking the phone and checking the lock screen a few seconds later the progress indicator was in the same position as before I locked it.
If I leave the phone locked for several minutes and check the lock screen the live activity says "Task failed".
I haven't seen anything in the documentation regarding execution of tasks while the phone is locked. So I'm a bit confused if I encountered an iOS bug here?
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, could you help us?
We implemented a Flutter library that basically makes a call every x minutes if the app is in the background, but when I generate the version via TestFlight for testing, it doesn't work.
Can you help us understand why?
Below is a more detailed technical description.
Apple Developer Technical Support Request
Subject: BGTaskScheduler / Background Tasks Not Executing in TestFlight - Flutter App with workmanager Plugin
Issue Summary
Background tasks scheduled using BGTaskScheduler are not executing when the app is distributed via TestFlight. The same implementation works correctly when running the app locally via USB/Xcode debugging.
We are developing a Flutter application that needs to perform periodic API calls when the app is in the background. We have followed all documentation and implemented the required configurations, but background tasks are not being executed in the TestFlight build.
App Information
Field
Value
App Version
3.1.15 (Build 311)
iOS Minimum Deployment Target
iOS 15.0
Framework
Flutter
Flutter SDK Version
^3.7.2
Technical Environment
Flutter Dependencies (Background Task Related)
Package
Version
Purpose
workmanager
^0.9.0+3
Main background task scheduler (uses BGTaskScheduler on iOS 13+)
flutter_background_service
^5.0.5
Background service management
flutter_background_service_android
^6.2.4
Android-specific background service
flutter_local_notifications
^19.4.2
Local notifications for background alerts
timezone
^0.10.0
Timezone support for scheduling
Other Relevant Flutter Dependencies
Package
Version
firebase_core
4.0.0
firebase_messaging
(via native Podfile)
sfmc (Salesforce Marketing Cloud)
^9.0.0
geolocator
^14.0.0
permission_handler
^12.0.0+1
Info.plist Configuration
We have added the following configurations to Info.plist:
UIBackgroundModes
<key>UIBackgroundModes</key>
<array>
<string>location</string>
<string>remote-notification</string>
<string>processing</string>
</array>
### BGTaskSchedulerPermittedIdentifiers
```xml
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>br.com.unidas.apprac.ios.workmanager.carrinho_api_task</string>
<string>br.com.unidas.apprac.ios.workmanager</string>
<string>be.tramckrijter.workmanager.BackgroundTask</string>
</array>
**Note:** We included multiple identifier formats as recommended by the `workmanager` Flutter plugin documentation:
1. `{bundleId}.ios.workmanager.{taskName}` - Custom task identifier
2. `{bundleId}.ios.workmanager` - Default workmanager identifier
3. `be.tramckrijter.workmanager.BackgroundTask` - Plugin's default identifier (as per plugin documentation)
## AppDelegate.swift Configuration
We have configured the `AppDelegate.swift` with the following background processing setup:
```swift
// In application(_:didFinishLaunchingWithOptions:)
// Configuration to enable background processing via WorkManager
// The "processing" mode in UIBackgroundModes allows WorkManager to use BGTaskScheduler (iOS 13+)
// This is required to execute scheduled tasks in background (e.g., API calls)
// Note: User still needs to have Background App Refresh enabled in iOS settings
if UIApplication.shared.backgroundRefreshStatus == .available {
// Allows iOS system to schedule background tasks with minimum interval
UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplication.backgroundFetchIntervalMinimum)
}
## WorkManager Implementation (Dart/Flutter)
### Initialization
```dart
/// Initializes WorkManager
static Future<void> initialize() async {
await Workmanager().initialize(callbackDispatcher, isInDebugMode: false);
print('WorkManagerService: WorkManager initialized');
}
### Task Registration
/// Schedules API execution after a specific delay
## Observed Behavior
### Works (Debug/USB Connection)
- When running the app via Xcode/USB debugging
- Background tasks are scheduled and executed as expected
- API calls are made successfully when the app is backgrounded
### Does NOT Work (TestFlight)
- When the app is distributed via TestFlight
- Background tasks appear to be scheduled (no errors in code)
- Tasks are **never executed** when the app is in background
- We have tested with:
- Background App Refresh enabled in iOS Settings
- App used frequently
- Device connected to WiFi and charging
- Waited for extended periods (hours)
## Possible heart points
1. **Are there any additional configurations required for `BGTaskScheduler` to work in TestFlight/Production builds that are not required for debug builds?**
2. **Is the identifier format correct?** We are using:
`br.com.unidas.apprac.ios.workmanager.carrinho_api_task`
- Should it match exactly with the task name registered in code?
3. **Are there any known issues with Flutter's `workmanager` plugin and iOS BGTaskScheduler in production environments?**
4. **Is there any way to verify through logs or system diagnostics if the background tasks are being rejected by the system?**
5. **Could there be any conflict between our other background modes (`location`, `remote-notification`) and `processing`?**
6. **Does the Salesforce Marketing Cloud SDK (SFMC) interfere with BGTaskScheduler operations?**
## Additional Context
- We have verified that `Background App Refresh` is enabled for our app in iOS Settings
- The app has proper entitlements for push notifications and location services
- Firebase, SFMC (Salesforce Marketing Cloud), and other SDKs are properly configured
- The issue is **only** present in TestFlight builds, not in debug/USB-connected builds
## References
- [Apple Documentation - BGTaskScheduler](https://developer.apple.com/documentation/backgroundtasks/bgtaskscheduler)
- [Apple Documentation - Choosing Background Strategies](https://developer.apple.com/documentation/backgroundtasks/choosing_background_strategies_for_your_app)
Thank you
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Hi everyone,
I’m trying to register fonts system-wide using CTFontManagerRegisterFontURLs with the .persistent scope. The fonts are delivered through Apple-Hosted Background Assets (since On-Demand Resources are deprecated).
Process-level registration works perfectly, but persistent registration triggers a system “Install Fonts” prompt, and tapping Install causes the app to crash immediately.
I’m wondering if anyone has successfully used Apple-Hosted Background Assets to provide persistent, system-wide installable fonts, or if this is a current OS limitation/bug.
What I Expect
Fonts delivered through Apple-Hosted Background Assets should be eligible for system-wide installation
Tap “Install” should install fonts into Settings → Fonts just like app-bundled or ODR fonts
App should not crash
Why This Matters
According to:
WWDC 2019: Font Management and Text Scaling
Developers can build font provider apps that install fonts system-wide, using bundled or On-Demand Resources.
WWDC 2025: Discover Apple-Hosted Background Assets
On-Demand Resources are deprecated, and AHBAs are the modern replacement.
Therefore, persistent font installation via Apple-Hosted Background Assets appears to be the intended path moving forward.
Question
Is this a known limitation or bug in iOS?
Should .persistent font installation work with Apple-Hosted Background Assets?
Do we need additional entitlement, manifest configuration, or packaging rules?
Any guidance or confirmation from Apple engineers would be greatly appreciated.
Additional Info
I submitted a Feedback including a minimal reproducible sample project:
FB21109320
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
Core Text
Background Assets
Typography
Concurrency
As far as I understand, the main thread has a run loop.
When an iOS app launches, the process must keep the run loop running to stay alive.
Does that mean the main thread is the very first thread created when the process starts?
I have an app for macOS that is built using Mac Catalyst. I need to perform some background processing. I'm using BGProcessingTaskRequest to schedule the request. I have also integrated CKSyncEngine so I need that to be able to perform its normal background processing.
On iOS, when the user leaves the app, I can see a log message that the request was scheduled and a bit later I see log messages coming from the actual background task code.
On macOS I ran the app from Xcode. I then quit the app (Cmd-q). I can see the log message that the request was scheduled. But the actual task is never run. In my test, I ran my app on a MacBook Pro running macOS 26.0. When I quit the app, I checked the log file in the app sandbox and saw the message that the task was scheduled. About 20 minutes later I closed the lid on the MacBook Pro for the night. I did not power down, it just went to sleep. Roughly 10 hours later I opened the lid on the MacBook Pro, logged in, and checked the log file. It had not been updated since quitting the app. I should also mention that the laptop was not plugged in at all during this period.
My question is, does a Mac Catalyst app support background processing after the user quits the app? If so, how is it enabled?
The documentation for BGProcessingTaskRequest and BGProcessingTask show they are supported under Mac Catalyst, but I couldn't find any documentation in the Background Tasks section that mentioned anything specific to setup for Mac Catalyst.
Running the Settings app and going to General -> Login Items & Extension, I do not see my app under the App Background Activity section. Does it need to be listed there? If so, what steps are needed to get it there?
If this is all documented somewhere, I'd appreciate a link since I was not able to find anything specific to making this work under Mac Catalyst.
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
CloudKit
macOS
Mac Catalyst
Background Tasks
Hi,
I'll explain my question through how whatsapp does it.
When the phone is locked then whatsapp routes call through apple's native callkit
When unlocked, pressing accept essentially redirects to whatsapp and then whatsapp handles the call from there.
However, this component of unlock detection is what I'm not able to find any info about.
Essentially,
how i do it is:
let isPhoneLocked = !UIApplication.shared.isProtectedDataAvailable
isProtectedDataAvailable == true → device is unlocked
isProtectedDataAvailable == false → device is locked
The problem is that if the phone has been recently unlocked, then protected data is still available on the phone even after the lock for the next 10-40 seconds. So theres a false positive.
I want there to be a foolproof and robust way to do this. And I'm not entirely sure how
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
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.
Since macOS 26, including the latest 26.1, the menu bar icon does not show up for our app called Plover which is built with PySide6 (based on Qt 6) and runs via a relocatable python that is packaged into the app. The code is open source and can be found on GitHub. The latest release, including the notarized DMG, can be found here.
When running the .app via the command below, the menu bar icon does show up but the process that is running is python3.13 and not Plover:
/Applications/Plover.app/Contents/MacOS/Plover -l debug
When running the app by just clicking on the application icon, the process is Plover but the menu bar icon is not showing - also not in the settings (Menu Bar > Allow in the Menu Bar). Before macOS 26, the menu bar icon was always shown.
Some pointers to potentially relevant parts of our code:
shell script that builds the .app
Info.plist
plover_launcher.c
trayicon.py
This problem might be related to this thread, including the discussion around Qt not calling NSApplicationMain.
What I'm trying to figure out is whether this is a problem with macOS 26, Qt 6, PySide6, or our code.
Any pointers are highly appreciated!
Topic:
App & System Services
SubTopic:
Processes & Concurrency
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?
For example, let’s propose an XPC service that can connect to websites. Suppose that I want to connect to Apple.com, microsoft.com, and ibm.com. Can 3 service objects be made between the service and client? Or does the service have to return an ID for each web connection, with the client needing to specify which connection ID along with a command?
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.
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.
General:
Forums subtopic: App & System Services > Processes & Concurrency
Forums tag: Background Tasks
Background Tasks framework documentation
UIApplication background tasks documentation
ProcessInfo expiring activity documentation
Using background tasks documentation for watchOS
Performing long-running tasks on iOS and iPadOS documentation
WWDC 2020 Session 10063 Background execution demystified — This is critical resource. Watch it! [1]
WWDC 2022 Session 10142 Efficiency awaits: Background tasks in SwiftUI
WWDC 2025 Session 227 Finish tasks in the background — This contains an excellent summary of the expected use cases for each of the background task types.
iOS Background Execution Limits forums post
UIApplication Background Task Notes forums post
Testing and Debugging Code Running in the Background forums post
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] Sadly the video is currently not available from Apple. I’ve left the link in place just in case it comes back.
Hello! We are in the progress of migrating a large Swift 5.10 legacy code base over to use Swift 6.0 with Strict Concurrency checking.
We have already stumbled across a few weird edge cases where the "guaranteed" @MainActor isolation is violated (such as with @objc #selector methods used with NotificationCenter).
However, we recently found a new scenario where our app crashes accessing main actor isolated state on a background thread, and it was surprising that the compiler couldn't warn us.
Minimal reproducible example:
class ViewController: UIViewController {
var isolatedStateString = "Some main actor isolated state"
override func viewDidLoad() {
exampleMethod()
}
/// Note: A `@MainActor` isolated method in a `@MainActor` isolated class.
func exampleMethod() {
testAsyncMethod() { [weak self] in
// !!! Crash !!!
MainActor.assertIsolated()
// This callback inherits @MainActor from the class definition, but it is called on a background thread.
// It is an error to mutate main actor isolated state off the main thread...
self?.isolatedStateString = "Let me mutate my isolated state"
}
}
func testAsyncMethod(completionHandler: (@escaping () -> Void)) {
let group = DispatchGroup()
let queue = DispatchQueue.global()
// The compiler is totally fine with calling this on a background thread.
group.notify(queue: queue) {
completionHandler()
}
// The below code at least gives us a compiler warning to add `@Sendable` to our closure argument, which is helpful.
// DispatchQueue.global().async {
// completionHandler()
// }
}
}
The problem:
In the above code, the completionHandler implementation inherits main actor isolation from the UIViewController class.
However, when we call exampleMethod(), we crash because the completionHandler is called on a background thread via the DispatchGroup.notify(queue:).
If were to instead use DispatchQueue.global().async (snippet at the bottom of the sample), the compiler helpfully warns us that completionHandler must be Sendable.
Unfortunately, DispatchGroup's notify gives us no such compiler warnings. Thus, we crash at runtime.
So my questions are:
Why can't the compiler warn us about a potential problem with DispatchGroup().notify(queue:) like it can with DispatchQueue.global().async?
How can we address this problem in a holistic way in our app, as it's a very simple mistake to make (with very bad consequences) while we migrate off GCD?
I'm sure the broader answer here is "don't mix GCD and Concurrency", but unfortunately that's a little unavoidable as we migrate our large legacy code base! 🙂
I would like to know whether BGContinuedProcessingTaskRequest supports executing asynchronous tasks internally, or if it can only execute synchronous tasks within BGContinuedProcessingTaskRequest?
Our project is very complex, and we now need to use BGContinuedProcessingTaskRequest to perform some long-running operations when the app enters the background (such as video encoding/decoding & export). However, our export interface is an asynchronous function, for example video.export(callback: FinishCallback). This export call returns immediately, and when the export completes internally, it calls back through the passed-in callback. So when I call BGTaskScheduler.shared.register to register a BGContinuedProcessingTask, what should be the correct approach? Should I directly call video.export(nil) without any waiting, or should I wait for the export function to complete in the callback?
For example:
BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.xxx.xxx.xxx.xxx", using: nil) { task in
guard let continuedTask = task as? BGContinuedProcessingTask else {
task.setTaskCompleted(success: false)
return
}
let scanner = SmartAssetsManager.shared
let semaphore = DispatchSemaphore(value: 0)
continuedTask.expirationHandler = {
logError(items: "xwxdebug finished.")
semaphore.signal()
}
logInfo(items: "xwxdebug start!")
video.export { _ in
semaphore.signal()
}
semaphore.wait()
logError(items: "xwxdebug finished!")
}
TCC Permission Inheritance for Python Process Launched by Swift App in Enterprise Deployment
We are developing an enterprise monitoring application that requires a hybrid Swift + Python architecture due to strict JAMF deployment restrictions. We must deploy a macOS application via ABM/App Store Connect, but our core monitoring logic is in a Python daemon. We need to understand the feasibility and best practices for TCC permission inheritance in this specific setup.
Architecture
Component
Bundle ID
Role
Deployment
Swift Launcher
com.athena.AthenaSentry
Requests TCC permissions, launches Python child process.
Deployed via ABM/ASC.
Python Daemon
com.athena.AthenaSentry.Helper
Core monitoring logic using sensitive APIs.
Nested in Contents/Helpers/.
Both bundles are signed with the same Developer ID and share the same Team ID.
Required Permissions
The Python daemon needs to access the following sensitive TCC-controlled services:
Screen Recording (kTCCServiceScreenCapture) - for capturing screenshots.
Input Monitoring (kTCCServiceListenEvent) - for keystroke/mouse monitoring.
Accessibility (kTCCServiceAccessibility) - a prerequisite for Input Monitoring.
Attempts & Workarounds
We have attempted to resolve this using:
Entitlement Inheritance: Added com.apple.security.inherit to the Helper's entitlements.
Permission Proxy: Swift app maintains active event taps to try and "hold" the permissions for the child.
Foreground Flow: Keeping the Swift app in the foreground during permission requests.
Questions
Is this architecture supported? Can a Swift parent app successfully request TCC permissions that a child process can then use?
TCC Inheritance: What are the specific rules for TCC permission inheritance between parent/child processes in enterprise environment?
What's the correct approach for this enterprise use case? Should we:
Switch to a Single Swift App? (i.e., abandon the Python daemon and rewrite the core logic natively in Swift).
Use XPC Services? (instead of launching the child process directly).
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
Enterprise
Entitlements
Privacy
Scripting
Hello,
https://developer.apple.com/forums/thread/802443
https://developer.apple.com/documentation/servicemanagement/updating-helper-executables-from-earlier-versions-of-macos
https://developer.apple.com/documentation/ServiceManagement/updating-your-app-package-installer-to-use-the-new-service-management-api#Run-the-sample-launch-agent
Read these.
Earlier we had a setup with SMJobBless, now we have migrated to SMAppService.
Everything is working fine, the new API seems easier to manage, but we are having issues with updating the daemon.
I was wondering, what is the right process for updating a daemon from app side?
What we are doing so far:
App asks daemon for version
If version is lower than expected:
daemon.unregister(), wait a second and daemon.register() again.
The why?
We have noticed that unregistering/registering multiple times, of same daemon, can cause the daemon to stop working as expected. The daemon toggle in Mac Settings -> Login Items & Extensions can be on or off, but the app can still pickup daemon running, but no daemon running in Activity monitor. Registration/unregistration can start failing and nothing helps to resolve this, only reseting with sfltool resetbtm and a restart seems to does the job. This is usually noticeable for test users, testing same daemon version with different app builds.
In production app, we also increase the bundle version of daemon in plist, in test apps we - don't.
I haven't found any sources of how the update of pre-bundled app daemon should work.
Initial idea is register/unregister, but from what I have observed, this seems to mess up after multiple registrations.
I have a theory, that sending the daemon a command to kill itself after app update, would load the latest daemon.
Also, I haven't observed for daemon, with different build versions to update automatically.
What is the right way to update a daemon with SMAppService setup?
Thank you in advance.
On macOS 15.7.1 I'm trying to install an XPC service outside the app (Developer ID). It mostly seems to go ok, but when I set Launch Constraints on Responsible, AMFI complains of a violation, saying the service is responsible for itself, and fails to launch. Removing that constraint (or adding the service itself to the constraint) works fine.
The service is an optional download, and installed to /Users/Shared with a LaunchAgent specifying the MachService. The service is correctly launched and seems to pass all codesigning, notarization, and other checks, but the Responsible isn't set to the "calling" app.
Is this broken, or working as intended?
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
XPC
Code Signing
Developer ID
Service Management
XPC is the preferred inter-process communication (IPC) mechanism on Apple platforms. XPC has three APIs:
The high-level NSXPCConnection API, for Objective-C and Swift
The low-level Swift API, introduced with macOS 14
The low-level C API, which, while callable from all languages, works best with C-based languages
General:
Forums subtopic: App & System Services > Processes & Concurrency
Forums tag: XPC
Creating XPC services documentation
NSXPCConnection class documentation
Low-level API documentation
XPC has extensive man pages — For the low-level API, start with the xpc man page; this is the original source for the XPC C API documentation and still contains titbits that you can’t find elsewhere. Also read the xpcservice.plist man page, which documents the property list format used by XPC services.
Daemons and Services Programming Guide archived documentation
WWDC 2012 Session 241 Cocoa Interprocess Communication with XPC — This is no longer available from the Apple Developer website )-:
Technote 2083 Daemons and Agents — It hasn’t been updated in… well… decades, but it’s still remarkably relevant.
TN3113 Testing and Debugging XPC Code With an Anonymous Listener
XPC and App-to-App Communication forums post
Validating Signature Of XPC Process forums post
This forums post summarises the options for bidirectional communication
This forums post explains the meaning of privileged flag
Related tags include:
Inter-process communication, for other IPC mechanisms
Service Management, for installing and uninstalling Service Management login items, launchd agents, and launchd daemons
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"