Processes & Concurrency

RSS for tag

Discover how the operating system manages multiple applications and processes simultaneously, ensuring smooth multitasking performance.

Concurrency Documentation

Posts under Processes & Concurrency subtopic

Post

Replies

Boosts

Views

Activity

How to check for cancellation of background task
When using the old withTaskCancellationHandler(operation:onCancel:isolation:) to run background tasks, you were notified that the background task gets cancelled via the handler being called. SwiftUI provides the backgroundTask(_:action:) modifier which looks quite handy. However how can I check if the background task will be cancelled to avoid being terminated by the system? I have tried to check that via Task.isCancelled but this always returns false no matter what. Is this not possible when using the modifier in which case I should file a bug report? Thanks for your help
0
0
273
Mar ’25
Spawning electron app from deamon process
Hi all, Our company has an application that runs on several machines, this app is launched via a deamon that keeps it alive. One of the feature of this app, is to start a headless electron application to run some tests. When spawning this electron application with the new arm64 OS, we are getting this issue: Silent Test Agent Worker exited with code: 133 [ERROR] [75873:0205/135842.347044:ERROR:mach_port_rendezvous.cc(384)] bootstrap_look_up com.hivestreaming.silenttestagent.MachPortRendezvousServer.1: Permission denied (1100) [ERROR] [75873:0205/135842.347417:ERROR:shared_memory_switch.cc(237)] No rendezvous client, terminating process (parent died?) [ERROR] [75872:0205/135842.347634:ERROR:mach_port_rendezvous.cc(384)] bootstrap_look_up com.hivestreaming.silenttestagent.MachPortRendezvousServer.1: Permission denied (1100) [ERROR] [75872:0205/135842.347976:ERROR:shared_memory_switch.cc(237)] No rendezvous client, terminating process (parent died?) Both application (main app and electron one) are signed and notarized, but it seems that there is some other permission issue. If we run the electron application manually, all runs as expected. I added the crash report as attachment CrashReport.log
2
0
536
Feb ’25
My system daemons are not getting launched in MacOS 15x post reboot
When I install my application, it installs fine and everything works alongwith all the system level daemons but when I reboot the system, none of my daemons are getting launched and this happens only on MacOS 15x, on older version it is working fine. In the system logs, I see that my daemons have been detected as legacy daemons by backgroundtaskmanagementd with Disposition [enabled, allowed, visible, notified] 2025-01-13 21:17:04.919128+0530 0x60e Default 0x0 205 0 backgroundtaskmanagementd: [com.apple.backgroundtaskmanagement:main] Type: legacy daemon (0x10010) 2025-01-13 21:17:04.919128+0530 0x60e Default 0x0 205 0 backgroundtaskmanagementd: [com.apple.backgroundtaskmanagement:main] Flags: [ legacy ] (0x1) 2025-01-13 21:17:04.919129+0530 0x60e Default 0x0 205 0 backgroundtaskmanagementd: [com.apple.backgroundtaskmanagement:main] Disposition: [enabled, allowed, visible, notified] (0xb) But later, it backgroundtaskmanagementd decides to disallow it. 2025-01-13 21:17:05.013202+0530 0x32d Default 0x4d6 89 0 smd: (BackgroundTaskManagement) [com.apple.backgroundtaskmanagement:main] getEffectiveDisposition: disposition=[enabled, disallowed, visible, notified], have LWCR=true 2025-01-13 21:17:05.013214+0530 0x32d Error 0x0 89 0 smd: [com.apple.xpc.smd:all] Legacy job is not allowed to launch: <private> status: 2 Is there anything changed in latest Mac OS which is causing this issue? Also what does this status 2 means. Can someone please help with this error? The plist has is true
3
0
355
Feb ’25
Why is xpc_connection_set_peer_code_signing_requirement() closing the connection instead of returning XPC_ERROR_PEER_CODE_SIGNING_REQUIREMENT?
I'm using libxpc in a C server and Swift client. I set up a code-signing requirement in the server using xpc_connection_set_peer_code_signing_requirement(). However, when the client doesn't meet the requirement, the server just closes the connection, and I get XPC_ERROR_CONNECTION_INTERRUPTED on the client side instead of XPC_ERROR_PEER_CODE_SIGNING_REQUIREMENT, making debugging harder. What I want: To receive XPC_ERROR_PEER_CODE_SIGNING_REQUIREMENT on the client when code-signing fails, for better debugging. What I’ve tried: Using xpc_connection_set_peer_code_signing_requirement(), but it causes the connection to be dropped immediately. Questions: Why does the server close the connection without sending the expected error? How can I receive the correct error on the client side? Are there any other methods for debugging code-signing failures with libxpc? Thanks for any insights!
1
0
485
Feb ’25
XPC Resources
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"
0
0
3.2k
Nov ’25
BGContinuedProcessingTask Notification Error
Hello im creating an expo module using this new API, but the problem i found currently testing this functionality is that when the task fails, the notification error doesn't go away and is always showing the failed task notification even if i start a new task and complete that one. I want to implement this module into the production app but i feel like having always the notification error might confuse our users or find it a bit bothersome. Is there a way for the users to remove this notification? Best regards!
1
0
97
Sep ’25
Concurrency Resources
Swift Concurrency Resources: Forums tags: Concurrency The Swift Programming Language > Concurrency documentation Migrating to Swift 6 documentation WWDC 2022 Session 110351 Eliminate data races using Swift Concurrency — This ‘sailing on the sea of concurrency’ talk is a great introduction to the fundamentals. WWDC 2021 Session 10134 Explore structured concurrency in Swift — The table that starts rolling out at around 25:45 is really helpful. Swift Async Algorithms package Swift Concurrency Proposal Index DevForum post Why is flow control important? forums post Dispatch Resources: Forums tags: Dispatch Dispatch documentation — Note that the Swift API and C API, while generally aligned, are different in many details. Make sure you select the right language at the top of the page. Dispatch man pages — While the standard Dispatch documentation is good, you can still find some great tidbits in the man pages. See Reading UNIX Manual Pages. Start by reading dispatch in section 3. WWDC 2015 Session 718 Building Responsive and Efficient Apps with GCD [1] WWDC 2017 Session 706 Modernizing Grand Central Dispatch Usage [1] Avoid Dispatch Global Concurrent Queues forums post Waiting for an Async Result in a Synchronous Function forums post Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" [1] These videos may or may not be available from Apple. If not, the URL should help you locate other sources of this info.
0
0
2.0k
4d
Prevent my app from background activity
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.
1
0
95
May ’25
How to safely maximize concurrent UI rendering
I'm using Swift 6 and tasks to concurrently process multiple PDF files for rendering, and it's working well. But currently I'm manually limiting the number of simultaneous tasks to 2 out of fear that the system might run many tasks concurrently without having enough RAM to do the PDF processing. Testing on a variety of devices, I've tried increasing the task limit and haven't seen any crashes, but I'm quite concerned about the possibility. Any given device might be using a lot of RAM at any moment, and any given PDF might strain resources more than the average PDF. Is there a recommended technique for handling this kind of scenario? Should I not worry about it and just go ahead and start a high number of tasks, trusting that the system won't run too many concurrently and therefore won't run out of RAM?
2
0
274
Mar ’25
Background Assets Extension and DeviceCheck
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?
2
0
150
May ’25
Did GCD change in macOS 26
Some users of my Mac app are complaining of redrawing delays. Based on what I see in logs, my GCD timer event handlers are not being run in a timely manner although the runloop is still pumping events: sometimes 500ms pass before a 15ms timer runs. During this time, many keypresses are routed through -[NSApplication sendEvent:], which is how I know it's not locked up in synchronous code. This issue has not been reported in older versions of macOS. I start the timer like this: _gcdUpdateTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); dispatch_source_set_timer(_gcdUpdateTimer, dispatch_time(DISPATCH_TIME_NOW, period * NSEC_PER_SEC), period * NSEC_PER_SEC, 0.0005 * NSEC_PER_SEC); dispatch_source_set_event_handler(_gcdUpdateTimer, ^{ …redraw… });
1
0
114
Sep ’25
MacOS 26 TestFlight SIGKILLs app when updating
We're developing an Electron app for MacOS App Store. When updating our app through TestFlight, TestFlight prompts "Close This App to Update", and when I click "Continue" our app would be "Terminated" for update. Now this is where things go wrong. On MacOS 15 our app seems to be gracefully terminating (We attached it with lldb and it shows that our app returns with 0 when we click "Continue") which is fine. However for MacOS 26 though, it seems that TestFlight just directly SIGKILLs our app (indicated by lldb), which means that all of our app's child processes are left orphaned. Even worse, our app is singleton, which means that when the app relaunches it fails, because the leftover child processes from the previously SIGKILLed session is still alive, and even if we want to kill those orphaned child processes we can't because our app is sandboxed thus cannot kill processes outside of the current sandbox. We captured output from log stream (app name redacted): 12-02 22:08:16.477036-0800 0x5452     Default     0x5a4a7              677    7    installcoordinationd: [com.apple.installcoordination:daemon] -[IXSCoordinatorProgress setTotalUnitsCompleted:]: Progress for coordinator: [com.our.app/Invalid/[user-defined//Applications/OurApp.app]], Phase: IXCoordinatorProgressPhaseLoading, Percentage: 99.454 123: Attempt to set units completed on finished progress: 214095161 2025-12-02 22:08:16.483056-0800 0x53ba     Default     0x5a5c9              167    0    runningboardd: (RunningBoard) [com.apple.runningboard:connection] Received termination request from [osservice<com.apple.installcoordinationd(274)>:677] on <RBSProcessPredicate <RBSProcessBundleIdentifierPredicate "com.our.app">> with context <RBSTerminateContext| explanation:installcoordinationd app:[com.our.app/Invalid/[user-defined//Applications/OurApp.app]] uuid:A3BC0629-124E-4165-ABB7-1324380FC354 isPlaceholder:N re portType:None maxTerminationResistance:Absolute attrs:[ 2025-12-02 22:08:16.488651-0800 0x53ba     Default     0x5a5c9              167    7    runningboardd: (RunningBoard) [com.apple.runningboard:ttl] Acquiring assertion targeting system from originator [osservice<com.apple.installcoordinationd(274)>:677] with description <RBSAssertionDescriptor| "installcoordinationd app:[com.our.app/Invalid/[user-defined//Applications/OurApp.app]] uuid:A3BC0629-124E-4165-ABB7-1324380FC354 isPlaceholder:N" ID:167-677-1463 target:system attributes:[ 2025-12-02 22:08:16.489353-0800 0x53ba     Default     0x5a5c9              167    0    runningboardd: (RunningBoard) [com.apple.runningboard:process] [app<application.com.our.app.485547.485561(501)>:2470] Terminating with context: <RBSTerminateContext| explanation:installcoordinationd app:[com.our.app/Invalid/[user-defined//Applications/OurApp.app]] uuid:A3BC0629-124E-4165-ABB7-1324380FC354 isPlaceholder:N reportType:None maxTerminationResistance:Absolute attrs:[ 2025-12-02 22:10:23.920869-0800 0x5a5a     Default     0x5a4c6              674    14   appstoreagent: [com.apple.appstored:Library] [A95D57D7] Completed with 1 result: <ASDApp: 0xc932a8780>: {bundleID = com.our.app; completedUnitCount = 600; path = /Applications/OurApp.app; installed = 0} 2025-12-02 22:10:32.027304-0800 0x5ae5     Default     0x5a4c7              674    14   appstoreagent: [com.apple.appstored:Library] [BEB5F2FD] Completed with 1 result: <ASDApp: 0xc932a8780>: {bundleID = com.our.app; completedUnitCount = 600; path = /Applications/OurApp.app; installed = 0} 2025-12-02 22:10:36.542321-0800 0x5b81     Default     0x5a4c8              674    14   appstoreagent: [com.apple.appstored:Library] [185B9DD6] Completed with 1 result: <ASDApp: 0xc932a8780>: {bundleID = com.our.app; completedUnitCount = 600; path = /Applications/OurApp.app; installed = 0} The line "Terminating with context" seems suspicious. This line is not seen on MacOS 15, only MacOS 26. Is this documented behavior? If so, how can we handle this?
8
0
298
2w
Waiting for an Async Result in a Synchronous Function
This comes up over and over, here on the forums and elsewhere, so I thought I’d post my take on it. If you have questions or comments, start a new thread here on the forums. Put it in the App & System Services > Processes & Concurrency subtopic and tag it with Concurrency. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Waiting for an Async Result in a Synchronous Function On Apple platforms there is no good way for a synchronous function to wait on the result of an asynchronous function. Lemme say that again, with emphasis… On Apple platforms there is no good way for a synchronous function to wait on the result of an asynchronous function. This post dives into the details of this reality. Prime Offender Imagine you have an asynchronous function and you want to call it from a synchronous function: func someAsynchronous(input: Int, completionHandler: @escaping @Sendable (_ output: Int) -> Void) { … processes `input` asynchronously … … when its done, calls the completion handler with the result … } func mySynchronous(input: Int) -> Int { … calls `someAsynchronous(…)` … … waits for it to finish … … results the result … } There’s no good way to achieve this goal on Apple platforms. Every approach you might try has fundamental problems. A common approach is to do this working using a Dispatch semaphore: func mySynchronous(input: Int) -> Int { fatalError("DO NOT WRITE CODE LIKE THIS") let sem = DispatchSemaphore(value: 0) var result: Int? = nil someAsynchronous(input: input) { output in result = output sem.signal() } sem.wait() return result! } Note This code produces a warning in the Swift 5 language mode which turns into an error in the Swift 6 language mode. You can suppress that warning with, say, a Mutex. I didn’t do that here because I’m focused on a more fundamental issue here. This code works, up to a point. But it has unavoidable problems, ones that don’t show up in a basic test but can show up in the real world. The two biggest ones are: Priority inversion Thread pools I’ll cover each in turn. Priority Inversion Apple platforms have a mechanism that helps to prevent priority inversion by boosting the priority of a thread if it holds a resource that’s needed by a higher-priority thread. The code above defeats that mechanism because there’s no way for the system to know that the threads running the work started by someAsynchronous(…) are being waited on by the thread blocked in mySynchronous(…). So if that blocked thread has a high-priority, the system can’t boost the priority of the threads doing the work. This problem usually manifests in your app failing to meet real-time goals. An obvious example of this is scrolling. If you call mySynchronous(…) from the main thread, it might end up waiting longer than it should, resulting in noticeable hitches in the scrolling. Threads Pools A synchronous function, like mySynchronous(…) in the example above, can be called by any thread. If the thread is part of a thread pool, it consumes a valuable resource — that is, a thread from the pool — for a long period of time. The raises the possibility of thread exhaustion, that is, where the pool runs out of threads. There are two common thread pools on Apple platforms: Dispatch Swift concurrency These respond to this issue in different ways, both of which can cause you problems. Dispatch can choose to over-commit, that is, start a new worker thread to get work done while you’re hogging its existing worker threads. This causes two problems: It can lead to thread explosion, where Dispatch starts dozens and dozens of threads, which all end up blocked. This is a huge waste of resources, notably memory. Dispatch has an hard limit to how many worker threads it will create. If you cause it to over-commit too much, you’ll eventually hit that limit, putting you in the thread exhaustion state. In contrast, Swift concurrency’s thread pool doesn’t over-commit. It typically has one thread per CPU core. If you block one of those threads in code like mySynchronous(…), you limit its ability to get work done. If you do it too much, you end up in the thread exhaustion state. WARNING Thread exhaustion may seem like just a performance problem, but that’s not the case. It’s possible for thread exhaustion to lead to a deadlock, which blocks all thread pool work in your process forever. There’s a trade-off here. Swift concurrency doesn’t over-commit, so it can’t suffer from thread explosion but is more likely deadlock, and vice versa for Dispatch. Bargaining Code like the mySynchronous(…) function shown above is fundamentally problematic. I hope that the above has got you past the denial stage of this analysis. Now let’s discuss your bargaining options (-: Most folks don’t set out to write code like mySynchronous(…). Rather, they’re working on an existing codebase and they get to a point where they have to synchronously wait for an asynchronous result. At that point they have the choice of writing code like this or doing a major refactor. For example, imagine you’re calling mySynchronous(…) from the main thread in order to update a view. You could go down the problematic path, or you could refactor your code so that: The current value is always available to the main thread. The asynchronous code updates that value in an observable way. The main thread code responds to that notification by updating the view from the current value. This refactoring may or may not be feasible given your product’s current architecture and timeline. And if that’s the case, you might end up deploying code like mySynchronous(…). All engineering is about trade-offs. However, don’t fool yourself into thinking that this code is correct. Rather, make a note to revisit this choice in the future. Async to Async Finally, I want to clarify that the above is about synchronous functions. If you have a Swift async function, there is a good path forward. For example: func mySwiftAsync(input: Int) async -> Int { let result = await withCheckedContinuation { continuation in someAsynchronous(input: input) { output in continuation.resume(returning: output) } } return result } This looks like it’s blocking the current thread waiting for the result, but that’s not what happens under the covers. Rather, the Swift concurrency worker thread that calls mySwiftAsync(…) will return to the thread pool at the await. Later, when someAsynchronous(…) calls the completion handler and you resume the continuation, Swift will grab a worker thread from the pool to continue running mySwiftAsync(…). This is absolutely normal and doesn’t cause the sorts of problems you see with mySynchronous(…). IMPORTANT To keep things simple I didn’t implement cancellation in mySwiftAsync(…). In a real product it’s important to support cancellation in code like this. See the withTaskCancellationHandler(operation:onCancel:isolation:) function for the details.
0
0
826
Oct ’25
Running processing task for data upload together with state restoration
Hi All, In continuation of this thread https://developer.apple.com/forums/thread/804439 I want to perform data upload after getting it from the BLE device. As state restoration wake should not deal with data upload i though of using a processing task to perform the data upload. So the flow will be something like: Connect to device -> listen to notification -> go to background -> wake from notification -> handle data download from ble device -> register processing task for data upload -> hopefully get the data uploaded From reading about processing task i understand that the task execution is completely handled by the OS and depends on user behaviour and app usage. I even saw that if the user is not using the app for a while, the OS might not even perfoirm the task. So my quesiton is: does state restoration wakeup and perfroming data dowloads in the backgound considered app usage that will increase the likeluhood the task will get execution time? Can we rely on this for a scenario that the user opens the app for the first time, register, onboard for ble, connect to devie and then put it in the background for days or weeks and only relying on state restoration and processing tasks to do their thing? Sorry for the long read and appreciate your support! Shimon
1
0
85
Dec ’25
BGContinuedProcessingTask UI
When I use BGContinuedProcessingTask to submit a task, my iPhone 12 immediately shows a notification banner displaying the task’s progress. However, on my iPhone 15 Pro Max, there’s no response — the progress UI only appears in the Dynamic Island after I background the app. Why is there a difference in behavior between these two devices? Is it possible to control the UI so that the progress indicator only appears when the app moves to the background?
2
0
230
Oct ’25
SSMenuAgent consuming lots of CPU
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?
2
0
454
May ’25
Getting Started with SMAppService
I was stuck on a long train journey this weekend, so I thought I’d use that time to write up the process for installing a launchd daemon using SMAppService. This involves a number of deliberate steps and, while the overall process isn’t too hard — it’s certainly a lot better than with the older SMJobBless — it’s easy to accidentally stray from the path and get very confused. If you have questions or comments, start a new thread in the App & System Services > Processes & Concurrency subtopic and tag it with Service Management. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Getting Started with SMAppService This post explains how to use SMAppService to install a launchd daemon. I tested these instructions using Xcode 26.0 on macOS 15.6.1. Things are likely to be slightly different with different Xcode and macOS versions. Create the container app target To start, I created a new project: I choose File > New > Project. In the template picker, I chose macOS > App. In options page, I set the Product Name field to SMAppServiceTest [1]. And I selected my team in the Team popup. And I verified that the Organization Identifier was set to com.example.apple-samplecode, the standard for Apple sample code [1]. I selected SwiftUI in the Interface popup. There’s no requirement to use SwiftUI here; I chose it because that’s what I generally use these days. And None in the Testing System popup. And None in the Storage popup. I then completed the new project workflow. I configured basic settings on the project: In the Project navigator, I selected the SMAppServiceTest project. In the Project editor, I selected the SMAppServiceTest target. At the top I selected Signing & Capabilities. In the Signing section, I made sure that “Automatically manage signing” was checked. And that my team was selected in the Team popup. And that the bundle ID of the app ended up as com.example.apple-samplecode.SMAppServiceTest. Still in the Signing & Capabilities tab, I removed the App Sandbox section. Note It’s possible to use SMAppService to install a daemon from a sandboxed app, but in that case the daemon also has to be sandboxed. That complicates things, so I’m disabling the sandbox for the moment. See Enable App Sandbox, below, for more on this. Next I tweaked some settings to make it easier to keep track of which target is which: At the top, I selected the Build Settings tab. I changed the Product Name build setting from $(TARGET_NAME) to SMAppServiceTest. On the left, I renamed the target to App. I chose Product > Scheme > Manage Schemes. In the resulting sheet, I renamed the scheme from SMAppServiceTest to App, just to keep things in sync. [1] You are free to choose your own value, of course. However, those values affect other values later in the process, so I’m giving the specific values I used so that you can see how everything lines up. Create the daemon target I then created a daemon target: I chose File > New > Target. In the template picker, I chose macOS > Command Line Tool. In the options page, I set the Product Name field to Daemon. And I selected my team in the Team popup. And I verified that the Organization Identifier was set to com.example.apple-samplecode, the standard for Apple sample code. I selected Swift in the Language popup. And verified that SMAppServiceTest was set in the Project popup. I clicked Finish. I configured basic settings on the target: In the Project navigator, I selected the SMAppServiceTest project. In the Project editor, I selected the Daemon target. At the top I selected Signing & Capabilities. In the Signing section, I made sure that “Automatically manage signing” was checked. And that my team was selected in the Team popup. Note The Bundle Identifier field is blank, and that’s fine. There are cases where you want to give a daemon a bundle identifier, but it’s not necessary in this case. Next I tweaked some settings to make it easier to keep track of which target is which: At the top, I selected the Build Settings tab. I changed the Product Name build setting from $(TARGET_NAME) to SMAppServiceTest-Daemon. I forced the Enable Debug Dylib Support to No. IMPORTANT To set it to No, you first have to set it to Yes and then set it back to No. I edited Daemon/swift.swift to look like this: import Foundation import os.log let log = Logger(subsystem: "com.example.apple-samplecode.SMAppServiceTest", category: "daemon") func main() { log.log("Hello Cruel World!") dispatchMain() } main() This just logs a ‘first light’ log message and parks [1] the main thread in dispatchMain(). Note For more about first light log points, see Debugging a Network Extension Provider. [1] Technically the main thread terminates in this case, but I say “parks” because that’s easier to understand (-: Test the daemon executable I selected the Daemon scheme and chose Product > Run. The program ran, logging its first light log entry, and then started waiting indefinitely. Note Weirdly, in some cases the first time I ran the program I couldn’t see its log output. I had to stop and re-run it. I’m not sure what that’s about. I chose Product > Stop to stop it. I then switched back the App scheme. Embed the daemon in the app I added a build phase to embed the daemon executable into app: In the Project navigator, I selected the SMAppServiceTest project. In the Project editor, I selected the App target. At the top I selected Build Phases. I added a new copy files build phase. I renamed it to Embed Helper Tools. I set its Destination popup to Executables. I clicked the add (+) button under the list and selected SMAppServiceTest-Daemon. I made sure that Code Sign on Copy was checked for that. I then created a launchd property list file for the daemon: In the Project navigator, I selected SMAppServiceTestApp.swift. I chose Product > New > File from Template. I selected the Property List template. In the save sheet, I named the file com.example.apple-samplecode.SMAppServiceTest-Daemon.plist. And made sure that the Group popup was set to SMAppServiceTest. And that only the App target was checked in the Targets list. I clicked Create to create the file. In the property list editor, I added two properties: Label, with a string value of com.example.apple-samplecode.SMAppServiceTest-Daemon BundleProgram, with a string value of Contents/MacOS/SMAppServiceTest-Daemon I added a build phase to copy that property list into app: In the Project navigator, I selected the SMAppServiceTest project. In the Project editor, I selected the App target. At the top I selected Build Phases. I added a new copy files build phase. I renamed it to Copy LaunchDaemons Property Lists. I set its Destination popup to Wrapper. And set the Subpath field to Contents/Library/LaunchDaemons. I disclosed the contents of the Copy Bundle Resources build phase. I dragged com.example.apple-samplecode.SMAppServiceTest-Daemon.plist from the Copy Bundle Resources build phase to the new Copy LaunchDaemons Property Lists build phase. I made sure that Code Sign on Copy was unchecked. Register and unregister the daemon In the Project navigator, I selected ContentView.swift and added the following to the imports section: import os.log import ServiceManagement I then added this global variable: let log = Logger(subsystem: "com.example.apple-samplecode.SMAppServiceTest", category: "app") Finally, I added this code to the VStack: Button("Register") { do { log.log("will register") let service = SMAppService.daemon(plistName: "com.example.apple-samplecode.SMAppServiceTest-Daemon.plist") try service.register() log.log("did register") } catch let error as NSError { log.log("did not register, \(error.domain, privacy: .public) / \(error.code)") } } Button("Unregister") { do { log.log("will unregister") let service = SMAppService.daemon(plistName: "com.example.apple-samplecode.SMAppServiceTest-Daemon.plist") try service.unregister() log.log("did unregister") } catch let error as NSError { log.log("did not unregister, \(error.domain, privacy: .public) / \(error.code)") } } IMPORTANT None of this is code is structured as I would structure a real app. Rather, this is the absolutely minimal code needed to demonstrate this API. Check the app structure I chose Product > Build and verified that everything built OK. I then verified that the app’s was structured correctly: I then choose Product > Show Build Folder in Finder. I opened a Terminal window for that folder. In Terminal, I changed into the Products/Debug directory and dumped the structure of the app: % cd "Products/Debug" % find "SMAppServiceTest.app" SMAppServiceTest.app SMAppServiceTest.app/Contents SMAppServiceTest.app/Contents/_CodeSignature SMAppServiceTest.app/Contents/_CodeSignature/CodeResources SMAppServiceTest.app/Contents/MacOS SMAppServiceTest.app/Contents/MacOS/SMAppServiceTest.debug.dylib SMAppServiceTest.app/Contents/MacOS/SMAppServiceTest SMAppServiceTest.app/Contents/MacOS/__preview.dylib SMAppServiceTest.app/Contents/MacOS/SMAppServiceTest-Daemon SMAppServiceTest.app/Contents/Resources SMAppServiceTest.app/Contents/Library SMAppServiceTest.app/Contents/Library/LaunchDaemons SMAppServiceTest.app/Contents/Library/LaunchDaemons/com.example.apple-samplecode.SMAppServiceTest-Daemon.plist SMAppServiceTest.app/Contents/Info.plist SMAppServiceTest.app/Contents/PkgInfo There are a few things to note here: The com.example.apple-samplecode.SMAppServiceTest-Daemon.plist property list is in Contents/Library/LaunchDaemons. The daemon executable is at Contents/MacOS/SMAppServiceTest-Daemon. The app is still built as debug dynamic library (SMAppServiceTest.debug.dylib) but the daemon is not. Test registration I chose Product > Run. In the app I clicked the Register button. The program logged: will register did not register, SMAppServiceErrorDomain / 1 Error 1 indicates that installing a daemon hasn’t been approved by the user. The system also presented a notification: Background Items Added “SMAppServiceTest” added items that can run in the background for all users. Do you want to allow this? Options > Allow > Don’t Allow I chose Allow and authenticated the configuration change. In Terminal, I verified that the launchd daemon was loaded: % sudo launchctl list com.example.apple-samplecode.SMAppServiceTest-Daemon { "LimitLoadToSessionType" = "System"; "Label" = "com.example.apple-samplecode.SMAppServiceTest-Daemon"; "OnDemand" = true; "LastExitStatus" = 0; "Program" = "Contents/MacOS/SMAppServiceTest-Daemon"; }; IMPORTANT Use sudo to target the global launchd context. If you omit this you end up targeting the launchd context in which Terminal is running, a GUI login context, and you won't find any launchd daemons there. I started monitoring the system log: I launched the Console app. I pasted subsystem:com.example.apple-samplecode.SMAppServiceTest into the search box. I clicked “Start streaming”. Back in Terminal, I started the daemon: % sudo launchctl start com.example.apple-samplecode.SMAppServiceTest-Daemon In Console, I saw it log its first light log point: type: default time: 17:42:20.626447+0100 process: SMAppServiceTest-Daemon subsystem: com.example.apple-samplecode.SMAppServiceTest category: daemon message: Hello Cruel World! Note I’m starting the daemon manually because my goal here is to show how to use SMAppService, not how to use XPC to talk to a daemon. For general advice about XPC, see XPC Resources. Clean up Back in the app, I clicked Unregister. The program logged: will unregister did unregister In Terminal, I confirmed that the launchd daemon was unloaded: % sudo launchctl list com.example.apple-samplecode.SMAppServiceTest-Daemon Could not find service "com.example.apple-samplecode.SMAppServiceTest-Daemon" in domain for system Note This doesn’t clean up completely. The system remembers your response to the Background Items Added notification, so the next time you run the app and register your daemon it will be immediately available. To reset that state, run the sfltool with the resetbtm subcommand. Install an Agent Rather Than a Daemon The above process shows how to install a launchd daemon. Tweaking this to install a launchd agent is easy. There are only two required changes: In the Copy Launch Daemon Plists copy files build phase, set the Subpath field to Contents/Library/LaunchAgents. In ContentView.swift, change the two SMAppService.daemon(plistName:) calls to SMAppService.agent(plistName:). There are a bunch of other changes you should make, like renaming everything from daemon to agent, but those aren’t required to get your agent working. Enable App Sandbox In some cases you might want to sandbox the launchd job (the term job to refer to either a daemon or an agent.) This most commonly crops up with App Store apps, where the app itself must be sandboxed. If the app wants to install a launchd agent, that agent must also be sandboxed. However, there are actually four combinations, of which three are supported: App Sandboxed | Job Sandboxed | Supported ------------- | ------------- | --------- no | no | yes no | yes | yes yes | no | no [1] yes | yes | yes There are also two ways to sandbox the job: Continue to use a macOS > Command Line Tool target for the launchd job. Use an macOS > App target for the launchd job. In the first approach you have to use some low-level build settings to enable the App Sandbox. Specifically, you must assign the program a bundle ID and then embed an Info.plist into the executable via the Create Info.plist Section in Binary build setting. In the second approach you can use the standard Signing & Capabilities editor to give the job a bundle ID and enable the App Sandbox, but you have to adjust the BundleProgram property to account for the app-like wrapper. IMPORTANT The second approach is required if your launchd job uses restricted entitlements, that is, entitlements that must be authorised by a provisioning profile. In that case you need an app-like wrapper to give you a place to store the provisioning profile. For more on this idea, see Signing a daemon with a restricted entitlement. For more background on how provisioning profiles authorise the use of entitlements, see TN3125 Inside Code Signing: Provisioning Profiles. On balance, the second approach is the probably the best option for most developers. [1] When SMAppService was introduced it was possible to install a non-sandboxed daemon from a sandboxed app. That option is blocked by macOS 14.2 and later.
0
0
214
Sep ’25
About GCD (Grand Central Dispatch) in an extension.
We are currently developing a VoIP application that supports Local Push extention. I would like to ask for your advice on how the extension works when the iPhone goes into sleep mode. Our App are using GCD (Grand Central Dispatch) to perform periodic processing within the extension, creating a cycle by it. [sample of an our source] class LocalPushProvider: NEAppPushProvider { let activeQueue: DispatchQueue = DispatchQueue(label: "com.myapp.LocalPushProvider.ActiveQueue", autoreleaseFrequency: .workItem) var activeSchecule: Cancellable? override func start(completionHandler: @escaping (Error?) -&gt; Void) { : self.activeSchecule = self.activeQueue.schedule( after: .init(.now() + .seconds(10)), // start schedule after 10sec interval: .seconds(10) // interval 10sec ) { self.activeTimerProc() } completionHandler(nil) } } However In this App that we are confirming that when the iPhone goes into sleep mode, self.activeTimerProc() is not called at 10-second intervals, but is significantly delayed (approximately 30 to 180 seconds). What factors could be causing the timer processing using GCD not to be executed at the specified interval when the iPhone is in sleep mode? Also, please let us know if there are any implementation errors or points to note. I apologize for bothering you during your busy schedule, but I would appreciate your response.
3
0
121
Jun ’25
Push-to-Start Live Activity Background Task Issue After App Termination
Desired Behavior I want the app to be able to handle multiple Push-to-Start notifications even when it is completely terminated. Each Live Activity should: Be successfully displayed upon receiving a Push-to-Start notification. Trigger background tasks to send its update token to the server, regardless of the time interval between notifications. Problem I am facing an issue with iOS Live Activities when using Push-to-Start notifications to trigger Live Activities in an app that has been completely terminated. Here’s the detailed scenario: When the app is completely terminated and I send the first Push-to-Start notification: The Live Activity is successfully displayed. didFinishLaunchingWithOptions` is triggered, and background tasks execute correctly, including sending the update token to the server. When I send consecutive Push-to-Start notifications in quick succession (e.g., within a few seconds or minutes): Both notifications successfully display their respective Live Activities. Background tasks are executed correctly for both notifications. However, when there is a longer interval (e.g., 10 minutes) between two Push-to-Start notifications: The first notification works perfectly—it displays the Live Activity, triggers didFinishLaunchingWithOptions, and executes background tasks. The second notification successfully displays the Live Activity but fails to execute any background tasks, such as sending the update token to the server. My HypothesisI suspect that iOS might impose a restriction where background runtime for Push-to-Start notifications can only be granted once within a certain time frame after the app has been terminated. Any insights into why this issue might be occurring or how to ensure consistent background task execution for multiple Push-to-Start notifications would be greatly appreciated!
2
0
424
Mar ’25
How can I get a Subscriber to subscribe to get only 4 elements from an array?
Hello, I am trying to implement a subscriber which specifies its own demand for how many elements it wants to receive from a publisher. My code is below: import Combine var array = [1, 2, 3, 4, 5, 6, 7] struct ArraySubscriber<T>: Subscriber { typealias Input = T typealias Failure = Never let combineIdentifier = CombineIdentifier() func receive(subscription: any Subscription) { subscription.request(.max(4)) } func receive(_ input: T) -> Subscribers.Demand { print("input,", input) return .max(4) } func receive(completion: Subscribers.Completion<Never>) { switch completion { case .finished: print("publisher finished normally") case .failure(let failure): print("publisher failed due to, ", failure) } } } let subscriber = ArraySubscriber<Int>() array.publisher.subscribe(subscriber) According to Apple's documentation, I specify the demand inside the receive(subscription: any Subscription) method, see link. But when I run this code I get the following output: input, 1 input, 2 input, 3 input, 4 input, 5 input, 6 input, 7 publisher finished normally Instead, I expect the subscriber to only "receive" elements 1, 2, 3, 4 from the array. How can I accomplish this?
0
0
128
Aug ’25