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

Do i need to free memory for C strings obtained from xpc_dictionary_get_string?
I am using C APIs for XPC communication. When my XPC server gets a xpc_dictionary as a message, I use xpc_dictionary_get_string to get the string which is of type const char*. Afterwards, when I try to free up the memory for the string, I get an error. I could not find any details on why this happens. Does XPC handle the lifecycle of these C strings ? I did some tests to see the behaviour. The following code snippet prints a string temp before and after releasing the dictionary memory. char* string = "dummy-string"; xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_string(dict, "str", string); const char* temp = xpc_dictionary_get_string(reply, "str"); printf("temp before release: %s\n", temp); xpc_release(reply); printf("temp after release: %s\n", temp); output: # temp before release: dummy-string # temp after release: I tried to free the variable temp before and after releasing dict . char* string = "dummy-string"; xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_string(dict, "str", string); const char* temp = xpc_dictionary_get_string(dict, "str"); printf("temp before release: %s\n", temp); free((void *)temp); // case 1 xpc_release(dict); // free((void *)temp); // case 2 printf("temp after release: %s\n", temp); in both the cases i got the output: # temp before release: dummy-string # app(18502,0x1f02fc840) malloc: Double free of object 0x145004a20 # app(18502,0x1f02fc840) malloc: *** set a breakpoint in malloc_error_break to debug # SIGABRT: abort # PC=0x186953720 m=0 sigcode=0 # signal arrived during cgo execution # ... # ...
2
0
421
Feb ’25
Question about BGAppRefreshTask approach for medication scheduling app
I'm developing a medication scheduling app similar to Apple Health's Medications feature, and I'd like some input on my current approach to background tasks. In my app, when a user creates a medication, I generate ScheduledDose objects (with corresponding local notifications) for the next 2 weeks and save them to SwiftData. To ensure this 2-week window stays current, I've implemented a BGAppRefreshTask that runs daily to generate new doses as needed. My concern is whether BGAppRefreshTask is the appropriate mechanism for this purpose. Since I'm not making any network requests but rather generating and storing local data, I'm questioning if this is the right approach. I'm also wondering how Apple Health's Medications feature handles this kind of scheduling. Their app seems to maintain future doses regardless of app usage patterns. Has anyone implemented something similar or can suggest the best background execution API for this type of scenario? Thanks for any guidance you can provide.
2
0
148
Apr ’25
XPC codesign requirement crashes application
We have an application that sets a code signing requirement on a XPC connection between a File Provider extension and the main application. Only with a specific Developer ID certificate <DEVELOPER_ID_TEAM_IDENTIFIER> that designated requirement is not accepted and the application crashes with EXC_CRASH (SIGABRT) and the stacktrace Thread 1 Crashed:: Dispatch queue: com.apple.root.default-qos 0 libsystem_kernel.dylib 0x19b556388 __pthread_kill + 8 1 libsystem_pthread.dylib 0x19b58f88c pthread_kill + 296 2 libsystem_c.dylib 0x19b498a3c abort + 124 3 libc++abi.dylib 0x19b545384 abort_message + 132 4 libc++abi.dylib 0x19b533cf4 demangling_terminate_handler() + 344 5 libobjc.A.dylib 0x19b1b8dd4 _objc_terminate() + 156 6 libc++abi.dylib 0x19b544698 std::__terminate(void (*)()) + 16 7 libc++abi.dylib 0x19b547c30 __cxxabiv1::failed_throw(__cxxabiv1::__cxa_exception*) + 88 8 libc++abi.dylib 0x19b547bd8 __cxa_throw + 92 9 libobjc.A.dylib 0x19b1aecf8 objc_exception_throw + 448 10 Foundation 0x19d5c3840 -[NSXPCConnection setCodeSigningRequirement:] + 140 11 libxpcfileprovider.dylib 0x301023048 NSXPCConnection.setCodeSigningRequirementFromTeamIdentifier(_:) + 1796 12 libxpcfileprovider.dylib 0x30101dc94 closure #1 in CallbackFileProviderManager.getFileProviderConnection(_:service:completionHandler:interruptionHandler:exportedObject:) + 1936 13 libxpcfileprovider.dylib 0x30101e110 thunk for @escaping @callee_guaranteed @Sendable (@guaranteed NSXPCConnection?, @guaranteed Error?) -> () + 80 14 Foundation 0x19d46c3a4 __72-[NSFileProviderService getFileProviderConnectionWithCompletionHandler:]_block_invoke_2.687 + 284 15 libdispatch.dylib 0x19b3d7b2c _dispatch_call_block_and_release + 32 16 libdispatch.dylib 0x19b3f185c _dispatch_client_callout + 16 17 libdispatch.dylib 0x19b40e490 + 32 18 libdispatch.dylib 0x19b3e9fa4 _dispatch_root_queue_drain + 736 19 libdispatch.dylib 0x19b3ea5d4 _dispatch_worker_thread2 + 156 20 libsystem_pthread.dylib 0x19b58be28 _pthread_wqthread + 232 21 libsystem_pthread.dylib 0x19b58ab74 start_wqthread + 8 The designated codesign requirement on the XPC connection is set to anchor apple generic and certificate leaf[subject.OU] = <DEVELOPER_ID_TEAM_IDENTIFIER>" We have verified the designated code sign requirement to be valid on both the main bundle and the embedded extension using: codesign --verify -v -R '=anchor apple generic and certificate leaf[subject.OU] = "<DEVELOPER_ID_TEAM_IDENTIFIER>"' *.app codesign --verify -v -R '=anchor apple generic and certificate leaf[subject.OU] = "<DEVELOPER_ID_TEAM_IDENTIFIER>"' *.app/Contents/PlugIns/*
2
0
139
Sep ’25
BGContinuedProcessingTask launchHandler invocation
I'm trying to understand how the API works to perform a function that can continue running if the user closes the app. For a very simple example, consider a function that increments a number on screen every second, counting from 1 to 100, reaching completion at 100. The user can stay in the app for 100s watching it work to completion, or the user can close the app say after 2s and do other things while watching it work to completion in the Live Activity. To do this when the user taps a Start Counting button, you'd 1 Call BGTaskScheduler.shared.register(forTaskWithIdentifier:using:launchHandler:). Question 1: Do I understand correctly, all of the logic to perform this counting operation would exist entirely in the launchHandler block (noting you could call another function you define passing it the task to be able to update its progress)? I am confused because the documentation states "The system runs the block of code for the launch handler when it launches the app in the background." but the app is already open in the foreground. This made me think this block is not going to be invoked until the user closes the app to inform you it's okay to continue processing in the background, but how would you know where to pick up. I want to confirm my thinking was wrong, that all the logic should be in this block from start to completion of the operation, and it's fine even if the app stays in the foreground the whole time. 2 Then you'd create a BGContinuedProcessingTaskRequest and set request.strategy = .fail for this example because you need it to start immediately per the user's explicit tap on the Start Counting button. 3 Call BGTaskScheduler.shared.submit(request). Question 2: If the submit function throws an error, should you handle it by just performing the counting operation logic (call your function without passing a task)? I understand this can happen if for some reason the system couldn't immediately run it, like if there's already too many pending task requests. Seems you should not show an error message to the user, should still perform the request and just not support background continued processing for it (and perhaps consider showing a light warning "this operation can't be continued in the background so keep the app open"). Or should you still queue it up even though the user wants to start counting now? That leads to my next question Question 3: In what scenario would you not want the operation to start immediately (the queue behavior which is the default), given the app is already in the foreground and the user requested some operation? I'm struggling to think of an example, like a button titled Compress Photos Whenever You Can, and it may start immediately or maybe it won't? While waiting for the launchHandler to be invoked, should the UI just show 0% progress or "Pending" until the system can get to this task in the queue? Struggling to understand the use cases here, why make the user wait to start processing when they might not even intend to close the app during the operation? Thanks for any insights! As an aside, a sample project with a couple use cases would have been incredibly helpful to understand how the API is expected to be used.
8
0
321
Oct ’25
XPC between main app and system extension
Hello, I'm developing a Mac application that uses a network extension. I'm trying to implement XPC to pass data between my main app and system extension and I'm using the SimpleFirewall demo app as a guide to do this. One thing I can't understand is how the ViewController in the SimpleFirewall main app has access to the class IPCConnection in the SimpleFirewallExtension without it being public and without SimpleFirewallExtension being imported in ViewController.
1
0
583
Jan ’25
NSTask-launch path not accessible
I'm trying to launch a command line app from my objective C application (sandboxed) using NSTask and I keep getting "launch path not accessible" Here is the path: [task setLaunchPath:@"/usr/local/bin/codeview"]; I have set the appropriate attributes for codeview and it is working perfectly when I use it from the command line and /usr/local/bin IS in the $PATH I know I have NSTask configured correctly because this WILL work: [task setLaunchPath:@"/usr/bin/hexdump"]; With the exception being that I'm using a command already in /usr/bin. But I can't copy codeview into /usr/bin due to SIPS. I've tried moving codeview to various other non-SIPS protected locations all to no avail. Must all NSTask commands come from /usr/bin? Where might I put codeview so that it can be launched. Today I'm going to use an older computer and disable SIPS to put my command in /usr/bin and see if that works. If it does. I will do it on my main machine.
6
0
188
Apr ’25
[MacOS] Accessing underlying pthread or mach thread of NSThread
Is the title possible ? I tried [[thread valueForKey:@"_private"] valueForKey:@"tid"] but the tid was not kvc compliant. private apis are alright because this is just for testing remote process thread creation. I already have a working method but it has hardcoded assembly so you can't do anything else. this question is mainly for Quinn (figured he may know something about this)
2
0
324
Feb ’25
Control status item and login item from within app
In macOS 26 I noticed there is a section Menu Bar in System Settings which allows to toggle visibility of status items created with NSStatusItem. I'm assuming this is new, since I never noticed it before. Currently my app has a menu item that allows toggling its status item, but now I wonder whether it should always create the status item and let the user control its visibility from System Settings. Theoretically, keeping this option inside the app could lead to confusion if the user has previously disabled the status item in System Settings, then perhaps forgot about it, and then tries to enable it inside the app, but apparently nothing happens because System Settings overrides the app setting. Should I remove the option inside the app? This also makes me think of login items, which can be managed both in System Settings and inside the app via SMAppService. Some users ask why my app doesn't have a launch at login option, and I tell them that System Settings already offers that functionality. Since there is SMAppService I could offer an option inside the app that is kept in sync with System Settings, but I prefer to avoid duplicating functionality, particularly if it's something that is changed once by the user and then rarely (if ever) changed afterwards. But I wonder: why can login items be controlled by an app, and the status item cannot (at least I'm not aware of an API that allows to change the option in System Settings)? If the status item can be overridden in System Settings, why do login items behave differently?
7
0
169
Sep ’25
mac 开发 com.apple.security.application-groups 问题
我在开发 Mac应用完成 后 通过Xcode 上传二进制文件的过程中, 出现了错误, 错误提示: App里面用到的 com.apple.security.application-groups 权限里面 有 group.*** 和 开发者组ID.*** 导致校验失败, 当我单独使用 group.xxx的时候, 我的程序会崩溃 , 因为里面用到了 MachPortRende 进程间通信问题, 这里默认了 开发者组ID.*** 这个路径, 错误详情: 在尝试启动 QuickFox 应用时,程序因权限问题而崩溃。具体的错误信息 bootstrap_check_in 组ID.xxxx.MachPortRendezvousServer.82392: Permission denied (1100) 显示,应用在尝试使用 Mach 端口进行进程间通信时,没有获得足够的权限, 因此 我需要您们的帮助, 如果单独用开发者组ID.*** 我们又没有权限 将数据写入 组ID.xxx里面的文件
1
0
117
Apr ’25
C program posix_spawn diskutil fails with error -69877
Hello, I am programming a CLI tool to partition USB disks. I am calling diskutil to do the work, but I am hitting issues with permissions, it seems. Here is a trial run of the same command running diskutil directly on the terminal vs running from my code: Calling diskutil directly (works as expected) % /usr/sbin/diskutil partitionDisk /dev/disk2 MBR Free\ Space gap 2048S fat32 f-fix 100353S Free\ Space tail 0 Started partitioning on disk2 Unmounting disk Creating the partition map Waiting for partitions to activate Formatting disk2s1 as MS-DOS (FAT32) with name f-fix 512 bytes per physical sector /dev/rdisk2s1: 98784 sectors in 98784 FAT32 clusters (512 bytes/cluster) bps=512 spc=1 res=32 nft=2 mid=0xf8 spt=32 hds=16 hid=2079 drv=0x80 bsec=100360 bspf=772 rdcl=2 infs=1 bkbs=6 Mounting disk Finished partitioning on disk2 /dev/disk2 (disk image): #: TYPE NAME SIZE IDENTIFIER 0: FDisk_partition_scheme +104.9 MB disk2 1: DOS_FAT_32 F-FIX 51.4 MB disk2s1 Calling diskutil programmatically (error -69877) % sudo ./f-fix DEBUG: /usr/sbin/diskutil partitionDisk /dev/disk2 MBR Free Space gap 2048S fat32 f-fix 100353S Free Space tail 0 Started partitioning on disk2 Unmounting disk Error: -69877: Couldn't open device (Is a disk in use by a storage system such as AppleRAID, CoreStorage, or APFS?) Failed to fix drive `/dev/disk2' Source Code The relevant code from my program is this: char *args[16]; int n = 0; args[n++] = "/usr/sbin/diskutil"; args[n++] = "partitionDisk"; args[n++] = (char *)disk; args[n++] = (char *)scheme; (...) args[n++] = NULL; char **parent_env = *_NSGetEnviron(); if (posix_spawnp(&pid, args[0], NULL, NULL, args, parent_env) != 0) return 1; if (waitpid(pid, &status, 0) < 0) return 1; return 0; Question Are there any system protections against running it like so? What could I be missing? Is this a Disk Arbitration issue?
1
0
128
May ’25
How to force cancel a task that doesn't need cleanup and doesn't check for cancellation?
How can you force cancel a task that doesn't need cleanup and doesn't check for cancellation? If this cannot be done, would this be a useful addition to Swift? Here is the situation: The async method doesn't check for cancellation since it is not doing anything repetively (for example in a loop). For example, the method may be doing "try JSONDecoder().decode(Dictionary<String, ...>.self, from: data)" where data is a large amount. The method doesn't need cleanup. I would like the force cancellation to throw an error. I am already handling errors for the async method. My intended situation if that the user request the async method to get some JSON encoded data, but since it is taking longer that they are willing to wait, they would tap a cancellation button that the app provides.
1
0
101
May ’25
Memory visibility issue regards to shared data with Dispatch Queue
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); }
3
0
112
May ’25
Some issues and questions regarding the use of the BGContinuedProcessingTask API
Hi, I have been recently debugging the BGContinuedProcessingTask API and encountered some of the following issues. I hope you can provide some answers: First, let me explain my understanding of this API. I believe its purpose is to allow an app to trigger tasks that can be represented with progress indicators and require a certain amount of time to complete. After entering the background, these tasks can continue to be completed through the BGContinuedProcessingTask, preventing the system from terminating them before they are finished. In the launchHandler of the registration process, we only need to do a few things: Determine whether the actual business processing is still ongoing. Update the progress, title, and subtitle. Handle the expirationHandler. Set the task as completed. Here are some issues I encountered during my debugging process: After I called register and submit, the BGContinuedProcessingTask could not be triggered. The return values from my API calls were all normal. I tried different device models, and some could trigger the task normally, such as the 15 Pro Max and 12 Pro Max. However, there were also some models, such as the 17 Pro, 15 Pro, and 15, that could not trigger the task properly. Moreover, there was no additional error information to help locate the issue. The background task failed unexpectedly, but my app was still running normally. As I mentioned above, my launchHandler only retrieves the actual business status and updates it. If a background task fails unexpectedly while the app is still running normally, it can mislead users and degrade the user experience of the app. Others have also mentioned the issue of inconsistent behavior on devices that do not support Dynamic Island. On devices that support Dynamic Island, when a task is triggered in the foreground, the app does not immediately display a pop-up notification within the app. However, on devices that do not support Dynamic Island, the app directly displays a pop-up notification within the app, and this notification does not disappear when switching between different screens within the same app. The user needs to actively swipe up to dismiss it. I think this experience is too intrusive for users. I would like to know whether this will be maintained in the future or if there is a plan to fix it. On devices that do not support Dynamic Island, using the beta version 26.1 of the system, if the system is in dark mode but the app triggers a business interface in white, the pop-up notification will have the same color as the current page, making it difficult to read the content inside the pop-up. Users can actively stop background tasks by using the stop button, or the system can also stop tasks automatically when resources are insufficient or when a task is abnormal. However, according to the current API, all these actions are triggered through the expirationHandler. Currently, there is no way to distinguish whether the task was stopped by the user, by the system due to resource insufficiency, or due to an abnormal task. I would like to know whether there will be more information provided in the future to help distinguish these different scenarios. I believe that the user experience issues mentioned in points 2 and 3 are the most important. Please help to answer the questions and concerns above. Thank you!
7
0
283
Nov ’25
XPC Service Installed Outside App Doesn't Set Responsible
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?
3
0
233
Nov ’25
Background Download Not Working in release builds- flutter_downloader
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.
1
0
85
Nov ’25
Background Tasks runs foreground
Hello everyone! I'm having a problem with background tasks running in the foreground. When a user enters the app, a background task is triggered. I've written some code to check if the app is in the foreground and to prevent the task from running, but it doesn't always work. Sometimes the task runs in the background as expected, but other times it runs in the foreground, as I mentioned earlier. Could it be that I'm doing something wrong? Any suggestions would be appreciated. here is code: class BackgroundTaskService { @Environment(\.scenePhase) var scenePhase static let shared = BackgroundTaskService() private init() {} // MARK: - create task func createCheckTask() { let identifier = TaskIdentifier.check BGTaskScheduler.shared.getPendingTaskRequests { requests in if requests.contains(where: { $0.identifier == identifier.rawValue }) { return } self.createByInterval(identifier: identifier.rawValue, interval: identifier.interval) } } private func createByInterval(identifier: String, interval: TimeInterval) { let request = BGProcessingTaskRequest(identifier: identifier) request.earliestBeginDate = Date(timeIntervalSinceNow: interval) scheduleTask(request: request) } // MARK: submit task private func scheduleTask(request: BGProcessingTaskRequest) { do { try BGTaskScheduler.shared.submit(request) } catch { // some actions with error } } // MARK: background actions func checkTask(task: BGProcessingTask) { let today = Calendar.current.startOfDay(for: Date()) let lastExecutionDate = UserDefaults.standard.object(forKey: "lastCheckExecutionDate") as? Date ?? Date.distantPast let notRunnedToday = !Calendar.current.isDate(today, inSameDayAs: lastExecutionDate) guard notRunnedToday else { task.setTaskCompleted(success: true) createCheckTask() return } if scenePhase == .background { TaskActionStore.shared.getAction(for: task.identifier)?() } task.setTaskCompleted(success: true) UserDefaults.standard.set(today, forKey: "lastCheckExecutionDate") createCheckTask() } } And in AppDelegate: BGTaskScheduler.shared.register(forTaskWithIdentifier: "check", using: nil) { task in guard let task = task as? BGProcessingTask else { return } BackgroundTaskService.shared.checkNodeTask(task: task) } BackgroundTaskService.shared.createCheckTask()
1
0
940
Apr ’25
Using protocols with XPC C API instead of dictionaries for sending and receiving messages
I have followed this post for creating a Launch Agent that provides an XPC service on macOS using Swift- post link - https://rderik.com/blog/creating-a-launch-agent-that-provides-an-xpc-service-on-macos/ In the swift code the interface of the XPC service is defined by protocols which makes the code nice and neat. I want to implement the XPC service using C APIs for XPC, and C APIs send and receive messages using dictionaries, which need manual handling with conditional statements. I want to know if its possible to go with the protocol based approach with C APIs.
2
0
521
Jan ’25
earliestBeginDate timezone
I'm trying to schedule a background task that will run on an iPhone and I'm looking into creating a task request using BGProcessingTaskRequest and scheduled it using BGTaskScheduler.shared.submit(). Per earliestBeginDate documentation, this property can be used to specify the earliest time a background task will be launched by OS. All clear here. However, the question is: how is the value interpreted with respect to timezone ? Is the specified date in device timezone ? Is GMT ? Is something else ?
2
0
82
Apr ’25
BGTaskScheduler fails to match unique identifiers to a registered wildcard handler for BGContinuedProcessingTask
Testing Environment: iOS Version: 26.0 Beta 7 Xcode Version: 17.0 Beta 6 Device: iPhone 16 Pro Description: We are implementing the new BGContinuedProcessingTask API and are using the wildcard identifier notation as described in the official documentation. Our Info.plist is correctly configured with a permitted identifier pattern, such as com.our-bundle.export.*. We then register a single launch handler for this exact wildcard pattern. We are performing this registration within a UIViewController, which is a supported pattern as BGContinuedProcessingTask is explicitly exempt from the "register before applicationDidFinishLaunching" requirement, according to the BGTaskScheduler.h header file. The register method correctly returns true, indicating the registration was successful. However, when we then try to submit a task with a unique identifier that matches this pattern (e.g., com.our-bundle.export.UUID), the BGTaskScheduler.shared.submit() call throws an NSInternalInconsistencyException and terminates the app. The error reason is: 'No launch handler registered for task with identifier com.our-bundle.export.UUID'. This indicates that the system is not correctly matching the specific, unique identifier from the submit call to the registered wildcard pattern handler. This behavior contradicts the official documentation. Steps to Reproduce: Create a new Xcode project. In Signing & Capabilities, add "Background Modes" (with "Background processing" checked) and "Background GPU Access". Add a permitted identifier (e.g., "com.company.test.*") to BGTaskSchedulerPermittedIdentifiers in Info.plist. In a UIViewController's viewDidLoad, register a handler for the wildcard pattern. Check that the register method returns true. Immediately after, try to submit a BGContinuedProcessingTaskRequest with a unique identifier that matches the pattern. Expected Results: The submit call should succeed without crashing, and the task should be scheduled. Actual Results: The app crashes immediately upon calling submit(). The console shows an uncaught NSInternalInconsistencyException with the reason: 'No launch handler registered for task with identifier com.company.test.UUID'. Workaround: The issue can be bypassed if we register a new handler for each unique identifier immediately before submitting a request with that same unique identifier. This strongly suggests the bug is in the system's wildcard pattern-matching logic.
1
0
147
Sep ’25