Hi, I have a couple questions about background app refresh. First, is the function RefreshAppContentsOperation() where to implement code that needs to be run in the background? Second, despite importing BackgroundTasks, I am getting the error "cannot find operationQueue in scope". What can I do to resolve that? Thank you.
func scheduleAppRefresh() {
let request = BGAppRefreshTaskRequest(identifier: "peaceofmindmentalhealth.RoutineRefresh")
// Fetch no earlier than 15 minutes from now.
request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)
do {
try BGTaskScheduler.shared.submit(request)
} catch {
print("Could not schedule app refresh: \(error)")
}
}
func handleAppRefresh(task: BGAppRefreshTask) {
// Schedule a new refresh task.
scheduleAppRefresh()
// Create an operation that performs the main part of the background task.
let operation = RefreshAppContentsOperation()
// Provide the background task with an expiration handler that cancels the operation.
task.expirationHandler = {
operation.cancel()
}
// Inform the system that the background task is complete
// when the operation completes.
operation.completionBlock = {
task.setTaskCompleted(success: !operation.isCancelled)
}
// Start the operation.
operationQueue.addOperation(operation)
}
func RefreshAppContentsOperation() -> Operation {
}
How did we do? We’d love to know your thoughts on this year’s conference. Take the survey 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
It looks like ExtensionKit (and ExtensionFoundation) is fully available on iOS 26 but there is no mention about this in WWDC.
From my testing, it seems as of beta 1, ExtensionKit allows the app from one dev team to launch extension provided by another dev team. Before we start building on this, can someone from Apple help confirm this is the intentional behavior and not just beta 1 thing?
For a macOS GUI application (with a UIKit or AppKit entry point), I want to reliably capture diagnostic logs sent to stderr — especially useful when the app is launched from a terminal script or runs in the background, and traditional GUI elements (like alert dialogs) may not be viable. This is to log startup failures or even success messages for later inspection. However, when the app is launched via open MyApp.app, stderr redirection like open MyApp.app 2> log.txt does not capture any output — the file is created, but remains empty.
The only way I can capture stderr reliably is by bypassing the bundle and directly launching the binary inside with ./MyApp.app/Contents/MacOS/MyApp 2> ~/log.txt
This logs as expected, but is not the user-friendly or typical way to launch apps on macOS.
Double-clicking the app in Finder also does not show any stderr output.
Is there any recommended or supported way to redirect or access stderr output when launching a .app bundle via open, or any best practice for logging critical failures from a GUI app when terminal output isn't visible?
Topic:
App & System Services
SubTopic:
Processes & Concurrency
I have an app which contains a bundled launch agent that I register using SMAppService.agent(plistName:). I’ve packaged the launch agent executable in the typical Mac app bundle structure so I can embed a framework in it. So, the launch agent lives in Contents/SharedSupport/MyLaunchAgent.app/Contents/MacOS/MyLaunchAgent.
However, I suspect this approach might be falling afoul of the scheduler, since the taskinfo tool reports my launch agent has a requested & effective role of TASK_DEFAULT_APPLICATION (PRIO_DARWIN_ROLE_UI), rather than the TASK_UNSPECIFIED (PRIO_DARWIN_ROLE_DEFAULT) value I see with system daemons.
I tried setting the LSUIElement Info.plist key of my launch agent to YES, but this seems to have had no effect.
What’s the recommended approach here?
Hi,
I built an Electron app that uses puppeteer-cluster to open a bundled version of Chrome. Everything works before packaging/signing with electron builder. Transporter does not report any issues and the app opens in TestFlight.
the Chrome.app is signed separately before running builder
hardenedRuntime = false
However, a permission error occurs when cluster attempts to launch Chrome:
Error: Unable to launch browser, error message: Failed to launch the browser process!
[0601/152740.225314:ERROR:bootstrap.cc(65)] bootstrap_check_in org.chromium.crashpad.child_port_handshake.9915.63117.BUEXLMXFWPLCEONM: Permission denied (1100)
[0601/152740.226091:ERROR:file_io.cc(94)] ReadExactly: expected 4, observed 0
[0601/152740.229808:ERROR:bootstrap.cc(65)] bootstrap_check_in org.chromium.crashpad.child_port_handshake.9913.63115.VVKELOQUCWUYPFMQ: Permission denied (1100)
[0601/152740.230244:ERROR:file_io.cc(94)] ReadExactly: expected 4, observed 0
[9911:45571:0601/152740.506968:ERROR:named_platform_channel_mac.cc(44)] bootstrap_check_in com.google.chrome.for.testing.apps.52995c87946bbcc94fc9a27df1478a13: Permission denied (1100)
[9911:62467:0601/152740.507564:FATAL:mach_port_rendezvous.cc(281)] Check failed: kr == KERN_SUCCESS. bootstrap_check_in com.google.chrome.for.testing.MachPortRendezvousServer.9911: Permission denied (1100)
at Cluster.<anonymous> (/Applications/MyApp.app/Contents/Resources/app.asar/node_modules/puppeteer-cluster/dist/Cluster.js:119:23)
at Generator.throw (<anonymous>)
at rejected (/Applications/MyApp.app/Contents/Resources/app.asar/node_modules/puppeteer-cluster/dist/Cluster.js:6:65)
at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
I'm wondering if it's an issue with entitlements, or something more.
The entitlements.mas.plist (aside from identifiers):
com.apple.security.app-sandbox
com.apple.security.cs.allow-jit
com.apple.security.cs.allow-unsigned-executable-memory
com.apple.security.cs.allow-dyld-environment-variables
com.apple.security.network.client
com.apple.security.network.server
com.apple.security.files.user-selected.read-write
com.apple.security.cs.disable-executable-page-protection
com.apple.security.files.user-selected.executable
I've spent many hours searching for a solution. Any help or insight would be greatly appreciated.
Topic:
App & System Services
SubTopic:
Processes & Concurrency
I am developing a background application that acts as a metadata server under MacOS written in Swift. Sandboxed clients prompt the user to select URLs which are passed to the server as security scoped bookmarks via an App Group and the metadata will be passed back. I don't want the I/O overhead of passing the complete image file data to the server. All the variations I have tried of creating security scoped bookmarks in the client and reading them from the server fail with error messages such as "The file couldn’t be opened because it isn’t in the correct format." Can anyone guide me in the right direction or is this just not possible?
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
Files and Storage
App Sandbox
XPC
We've seen a recent increase in background terminations:
blue - System Pressure
orange - Task Timeout
I'm trying to understand the increase in system-pressure terminations, since there's no corresponding increase in memory at suspension. Are there other system resources for which iOS will terminate an app?
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
Organizer Window
Background Tasks
After logging in to the main App, turn on screen recording, then switch to the interface of another App to perform operations. After about ten-odd minutes, when returning to the main App, it was found that the app was forcefully quit by the system, and subsequent operations could not be carried out.
Hi,
I have a sandboxed app with a bundled sandboxed XPC service. When it’s launched, the XPC service registers a repeating XPC activity with the system. The activity’s handler block does get called regularly like I’d expect, but it stops being called once the main app terminates.
What’s the recommended way to fix this issue? Could I have a bundled XPC service double as a launch agent, or would that cause other problems?
I have been working on updating an old app that makes extensive use of Objective-C's NSTask. Now using Process in Swift, I'm trying to gather updates as the process runs, using readabilityHandler and availableData. However, my process tends to exit before all data has been read. I found this post entitled "Running a Child Process with Standard Input and Output" but it doesn't seem to address gathering output from long-running tasks. Is there a straightforward way to gather ongoing output from a long running task without it prematurely exiting?
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
Foundation
Inter-process communication
Hello,
I'm experiencing an issue with my app where it's being terminated by the system with a watchdog violation during back-to-back messaging operations. I've analyzed the crash logs but would appreciate additional insights on optimizing my approach. I'd appreciate any insights on how to resolve this problem.
Crash Details:
Exception Type: EXC_CRASH (SIGKILL)
Termination Reason: FRONTBOARD with code 0x8BADF00D
Error: "scene-update watchdog transgression: app exhausted real time allowance of 10.00 seconds"
Reproduction Steps:
User A initiates back-to-back messages to other User
User A's UI becomes unresponsive and eventually the app crashes.
Stack Trace Analysis:
The crash occurs on the main thread, which appears to be blocked waiting for a condition in the keyboard handling system. The thread is stuck in [UIKeyboardTaskQueue _lockWhenReadyForMainThread] and related methods, suggesting an issue with keyboard-related operations during the messaging process.
Crash Tag
Exception Type: EXC_CRASH (SIGKILL)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Termination Reason: FRONTBOARD 2343432205
<RBSTerminateContext| domain:10 code:0x8BADF00D explanation:scene-update watchdog transgression: app<com.msikodiak.eptt(AD934F8A-DF57-4B75-BE73-8CF1A9A8F856)>:301 exhausted real (wall clock) time allowance of 10.00 seconds
ProcessVisibility: Foreground
ProcessState: Running
WatchdogEvent: scene-update
WatchdogVisibility: Background
WatchdogCPUStatistics: (
"Elapsed total CPU time (seconds): 6.390 (user 3.640, system 2.750), 11% CPU",
"Elapsed application CPU time (seconds): 0.020, 0% CPU"
)
ThermalInfo: (
"Thermal Level: 0",
"Thermal State: nominal"
) reportType:CrashLog maxTerminationResistance:Interactive>
Triggered by Thread: 0
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x1e773d438 __psynch_cvwait + 8
1 libsystem_pthread.dylib 0x2210bc328 _pthread_cond_wait + 1028
2 Foundation 0x1957d8a64 -[NSCondition waitUntilDate:] + 132
3 Foundation 0x1957d8888 -[NSConditionLock lockWhenCondition:beforeDate:] + 80
4 UIKitCore 0x1998f1238 -[UIKeyboardTaskQueue _lockWhenReadyForMainThread] + 456
5 UIKitCore 0x19a3d775c __59-[UIKeyboardImpl updateAutocorrectPrompt:executionContext:]_block_invoke_9 + 28
6 UIKitCore 0x19986b084 -[UIKeyboardTaskQueue lockWhenReadyForMainThread] + 168
7 UIKitCore 0x19a3f2994 -[UIKeyboardTaskQueue waitUntilTaskIsFinished:] + 148
8 UIKitCore 0x19a3f2ac4 -[UIKeyboardTaskQueue performSingleTask:breadcrumb:] + 132
9 UIKitCore 0x199e2f7e4 -[_UIKeyboardStateManager updateForChangedSelection] + 144
10 UIKitCore 0x199e24200 -[_UIKeyboardStateManager invalidateTextEntryContextForTextInput:] + 92
11 WebKit 0x1ad52fa54 WebKit::PageClientImpl::didProgrammaticallyClearFocusedElement(WebCore::ElementContext&&) + 40
12 WebKit 0x1ad55adcc WebKit::WebPageProxy::didProgrammaticallyClearFocusedElement(WebCore::ElementContext&&) + 136
13 WebKit 0x1acec74e8 WebKit::WebPageProxy::didReceiveMessage(IPC::Connection&, IPC::Decoder&) + 18604
14 WebKit 0x1acd21184 IPC::MessageReceiverMap::dispatchMessage(IPC::Connection&, IPC::Decoder&) + 236
15 WebKit 0x1ace449b8 WebKit::WebProcessProxy::dispatchMessage(IPC::Connection&, IPC::Decoder&) + 40
16 WebKit 0x1ace44228 WebKit::WebProcessProxy::didReceiveMessage(IPC::Connection&, IPC::Decoder&) + 1764
17 WebKit 0x1acd1e904 IPC::Connection::dispatchMessage(WTF::UniqueRef<IPC::Decoder>) + 268
18 WebKit 0x1acd1e478 IPC::Connection::dispatchIncomingMessages() + 576
19 JavaScriptCore 0x1ae386b8c WTF::RunLoop::performWork() + 524
20 JavaScriptCore 0x1ae386960 WTF::RunLoop::performWork(void*) + 36
21 CoreFoundation 0x196badce4 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 28
22 CoreFoundation 0x196badc78 __CFRunLoopDoSource0 + 172
23 CoreFoundation 0x196bac9fc __CFRunLoopDoSources0 + 232
24 CoreFoundation 0x196babc3c __CFRunLoopRun + 840
25 CoreFoundation 0x196bd0700 CFRunLoopRunSpecific + 572
26 GraphicsServices 0x1e3711190 GSEventRunModal + 168
27 UIKitCore 0x1997ee240 -[UIApplication _run] + 816
28 UIKitCore 0x1997ec470 UIApplicationMain + 336
29 Telstra PTT 0x1004d30c8 main + 56
30 dyld 0x1bd5d3ad8 start + 5964
Say I want to sync a toggle in my app with SMAppService's .status property.
If the status changes from my app I can track it. But if user toggles it from System Settings, I don't see a notification so then the UI in my app is out of date.
The status property is not key value observable and there doesn't appear to be a SMAppServiceStatusDidChangeNotification ?
I can re-read it every time my app will become active but feels kind of wrong to do it this way.
For this code:
let status = try await container.accountStatus()
Seeing this error:
2025-05-08 15:32:00.945731-0500 localhost myAgent[2661]: (myDaemon.debug.dylib) [com.myDaemon.cli:networking] Error Domain=CKErrorDomain Code=6 "Error connecting to CloudKit daemon. This could happen for many reasons, for example a daemon exit, a device reboot, a race with the connection inactivity monitor, invalid entitlements, and more. Check the logs around this time to investigate the cause of this error." UserInfo={NSLocalizedDescription=Error connecting to CloudKit daemon. This could happen for many reasons, for example a daemon exit, a device reboot, a race with the connection inactivity monitor, invalid entitlements, and more. Check the logs around this time to investigate the cause of this error., CKRetryAfter=5, CKErrorDescription=Error connecting to CloudKit daemon. This could happen for many reasons, for example a daemon exit, a device reboot, a race with the connection inactivity monitor, invalid entitlements, and more. Check the logs around this time to investigate the cause of this error., NSUnderlyingError=0x600001bfc270 {Error Domain=NSCocoaErrorDomain Code=4099 UserInfo={NSDebugDescription=
I initially started the this process as System Daemon to see what would happen (which obviously does not have CloudKit features). Then moved it back to /Library/LaunchAgents/ and can't get rid of that error.
I see also following message from CloudKit daemon:
Ignoring failed attempt to get container proxy for <private>: Error Domain=NSCocoaErrorDomain Code=4099 UserInfo={NSDebugDescription=<private>}
Automatically retrying getting container proxy due to error for <private>: Error Domain=NSCocoaErrorDomain Code=4099 UserInfo={NSDebugDescription=<private>}
XPC connection interrupted for <private>
And this error for xpc service:
[0x130e074b0] failed to do a bootstrap look-up: xpc_error=[3: No such process]
If I start the same cli process directly from XCode, then it works just fine.
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
I am building a Mac app that launch a GUI helper app and use XPC to communicate between them.
Main app start a XPC Listener using NSXPCListener(machServiceName: "group.com.mycompany.myapp.xpc")
Launch the helper app
Helper app connect to the XPC service and listen command from main app.
What I observe is the app seems can start XPC listener while I run it via Xcode. If I run the app using TestFlight build, or via the compiled debug binary (same one that I use on Xcode), it cannot start the XPC service. Here is what I see in the Console:
[0x600000ef7570] activating connection: mach=true listener=true peer=false name=group.com.mycompany.myapp.xpc
[0x600000ef7570] listener failed to activate: xpc_error=[1: Operation not permitted]
Both main app and helper app are sandboxed and in the same App Group - if they were not, I cannot connect the helper app to main app. I can confirm the entitlement profiles did contain the app group.
If I start the main app via xcode, and then launch the helper app manually via Finder, the helper app can connect to the XPC and everything work.
It is not related to Release configuration, as the same binary work while I am debugging, but not when I open the binary manually.
For context, the main app is a Catalyst app, and helper app is an AppKit app. To start a XPC listener on Catalyst, I had do it in a AppKit bridge via bundle.
Given the app worked on Xcode, I believe this approach can work. I just cannot figure out why it only work while I am debugging.
Any pointer to debug this issue is greatly appreciated. Thanks!
My load average on a largely idle system is around 22, going up to 70 or so periodically; SSMenuAgent seems to be consuming lots of CPU (and, looking at spindump, it certainly seems busy), but... it's not happening on any other system whose screens I am observing. (Er, I know about load average limitations, the process is also consuming 70-98% CPU according to both top and Activity Monitor.)
Since this machine (although idle) has our network extension, I'm trying to figure out if this is due to that, or of this is generally expected. Anyone?
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Hi, I have some questions regarding the Background Assets Extension and DeviceCheck framework.
Goal: Ensure that only users who have purchased the app can access the server's API without any user authentication using for example DeviceCheck framework and within a Background Assets Extension.
My app relies on external assets, which I'm loading using the Background Assets Extension. I'm trying to determine if it's possible to obtain a challenge from the server and send a DeviceCheck assertion during this process within the Background Assets Extension.
So far, I only receive session-wide authentication challenges—specifically NSURLAuthenticationMethodServerTrust in the Background Assets Extensio. I’ve tested with Basic Auth (NSURLAuthenticationMethodHTTPBasic) just for experimentation, but the delegate
func backgroundDownload(
_ download: BADownload,
didReceive challenge: URLAuthenticationChallenge
) async -> (URLSession.AuthChallengeDisposition, URLCredential?)
is never called with that authentication method. It seems task-specific challenges aren't coming through at all.
Also, while the DCAppAttestService API appears to be available on macOS, DCAppAttestService.isSupported always returns false (in my testing), which suggests it's not actually supported on macOS. Can anyone confirm if that’s expected behavior?
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
Network
DeviceCheck
Background Assets
I have been playing with application bundled LaunchAgents:
I downloaded Apple sample code,
Run the sample code as is,
Tweaked the sample code a lot and changed the LaunchAgents IDs and Mach ports IDs,
Created new projects with the learnings, etc.
After deleting all the Xcode projects and related project products and rebooting my machine several times, I noticed the LaunchAgent are still hanging around in launchctl. If I write launchctl print-disabled gui/$UID (or user/$UID) I can see all my testing service-ids:
disabled services = {
"com.xpc.example.agent" => disabled
"io.dehesa.apple.app.agent" => disabled
"io.dehesa.sample.app.agent" => disabled
"io.dehesa.example.agent" => disabled
"io.dehesa.swift.xpc.updater" => disabled
"io.dehesa.swift.agent" => disabled
}
(there are more service-ids in that list, but I removed them for brevity purposes).
I can enable or disable them with launchctl enable/disable service-target, but I cannot really do anything else because their app bundle and therefore PLIST definition are not there anymore. How can I completely remove them from my system?
More worryingly, I noticed that if I try to create new projects with bundled LaunchAgents and try to reuse one of those service-ids, then the LaunchAgent will refuse to run (when it was running ok previously). The calls to SMAppService APIs such .agent(plistName:) and register() would work, though.
I am building an app that uses the SMAppService to register a LaunchDaemon that is bundled with my .app. I've got a priming flow created which walks the user through approving the service so that it will start on login.
However, I need to also be able to upgrade this background service if the user updates the app. To do this, I think I need to call unregisterAndReturnError and then registerAndReturnError.
From my testing, this seems to work correctly, but I have a concern. Will the user ever be prompted to re-authorize the LaunchDaemon that I am registering? If so, under what circumstances will that happen, and what does it look like (so that I can guide the user through it)?
I’m working with apple dispatch queue in C with the following design: multiple dispatch queues enqueue tasks into a shared context, and a dedicated dispatch queue (let’s call it dispatch queue A) processes these tasks. However, it seems this design has a memory visibility issue.
Here’s a simplified version of my setup:
I have a shared_context struct that holds:
task_lis: a list that stores tasks to be prioritized and run — this list is only modified/processed by dispatch queue A (a serially dispatch queue), so I don't lock around it.
cross_thread_tasks: a list that other queues push tasks into, protected by a lock.
Other dispatch queues call a function schedule_task that
locks and appends a new task to cross_thread_tasks
call dispatch_after_f() to schedule a process_task() on dispatch queue A
process_task() that processes the task_list and is repeatedly scheduled on dispatch queue A :
Swaps cross_thread_tasks into a local list (with locking).
Pushes the tasks into task_list.
Runs tasks from task_list.
Reschedules itself via dispatch_after_f().
Problem:
Sometimes the tasks pushed from other threads don’t seem to show up in task_list when process_task() runs. The task_list appears to be missing them, as if the cross-thread tasks aren’t visible. However, if the process_task() is dispatched from the same thread the tasks originate, everything works fine.
It seems to be a memory visibility or synchronization issue. Since I only lock around cross_thread_tasks, could it be that changes to task_list (even though modified on dispatch queue A only) are not being properly synchronized or visible across threads?
My questions
What’s the best practice to ensure shared context is consistently visible across threads when using dispatch queues? Is it mandatory to lock around all tasks? I would love to minimize/avoid lock if possible.
Any guidance, debugging tips, or architectural suggestions would be appreciated!
===============================
And here is pseudocode of my setup if it helps:
struct shared_data {
struct linked_list* task_list;
}
struct shared_context {
struct shared_data *data;
struct linked_list* cross_thread_tasks;
struct thread_mutex* lock; // lock is used to protect cross_thread_tasks
}
static void s_process_task(void* shared_context){
struct linked_list* local_tasks;
// lock and swap the cross_thread_tasks into a local linked list
lock(shared_context->lock)
swap(shared_context->cross_thread_tasks, local_tasks)
unlock(shared_context->lock)
// I didnt use lock to protect `shared_context->data` as they are only touched within dispatch queue A in this function.
for (task : local_tasks) {
linked_list_push(shared_context->data->task_list)
}
// If the `process_task()` block is dispatched from `schedule_task()` where the task is created, the `shared_context` will be able to access the task properly otherwise not.
for (task : shared_context->data->task_list) {
run_task_if_timestamp_is_now(task)
}
timestamp = get_next_timestamp(shared_context->data->task_list)
dispatch_after_f(timestamp, dispatch_queueA, shared_context, process_task);
}
// On dispatch queue B
static void schedule_task(struct task* task, void* shared_context) {
lock(shared_context->lock)
push(shared_context->cross_thread_tasks, task)
unlock(shared_context->lock)
timestamp = get_timestamp(task)
// we only dispatch the task if the timestamp < 1 second. We did this to avoid the dispatch queue schedule the task too far ahead and prevent the shutdown process. Therefore, not all task will be dispatched from the thread it created.
if(timestamp < 1 second)
dispatch_after_f(timestamp, dispatch_queueA, shared_context, process_task);
}