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.
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
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.
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.
Purpose
I want to use launchd to run a shell script asynchronously every minute, but I'm encountering an issue where the job does not run, and I receive the error "Bootstrap failed: 5: Input/output error". I need help identifying the cause of this issue and how to configure launchd correctly.
What I've done
Created the shell script (test_ls_save.sh)
The script is designed to list the contents of the desktop and save the output to a specified directory.
#!/bin/bash
DATE=$(date +%Y-%m-%d_%H-%M-%S)
SAVE_DIR=/Users/test/Desktop/personal/log_gather
FILE_NAME="ls_output_$DATE.log"
ls ~/Desktop > "$SAVE_DIR/$FILE_NAME"
echo "ls output saved to $SAVE_DIR/$FILE_NAME"
File permissions (ls -l output): -rwxr-xr-x 1 test staff 1234 Feb 17 10:00 /Users/test/Desktop/personal/log_gather/exec/test_ls_save.sh
Created the launchd plist file (com.test.logTest.plist)
The plist file is configured to execute the shell script every minute.
<key>Label</key>
<string>com.test.logTest</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-c</string>
/Users/test/Desktop/personal/log_gather/exec/test_ls_save.sh
</array>
<key>StartInterval</key>
<integer>60</integer> <!-- Run every minute -->
File permissions (ls -l output): -rwxr-xr-x 1 test staff 512 Feb 17 10:00 /Users/test/Library/LaunchAgents/com.test.logTest.plist
Ran the job with launchctl
I used the following command to load the plist file into launchd:
sudo launchctl bootstrap gui/$(id -u) /Users/test/Library/LaunchAgents/com.test.logTest.plist
pc spec
MacBook Pro
Apple M1
16 GB RAM
macOS 15.3 (Build 24D60)
what I know
The configuration has been set, but the launchd job is not running every minute as expected.
I don't believe there is a mistake with the path.
When I check the job using launchctl list, the job does not appear in the list.
I don't know where the error log files are supposed to be. I checked /var/log/system.log, but there are no error logs.
The .sh file runs fine by itself, but it cannot be executed via launchctl.
Want to ask
What could be the cause of the launchd job not running as expected?
Also, is there a way to check where the logs are being output?
If there is an error in the plist file configuration, which part should be modified?
Specifically, what improvements should be made regarding environment variables and path settings?
If my information is not enough, please tell me what is not enough!
If I create a BGContinuedProcessingTaskRequest, register it, and then "do work" within it appropriately reporting progress, and before my task has finished doing all the work it had to do, its expirationHandler triggers...
does the task later try again?
Or does it lose the execution opportunity until the app is next re-launched to the foreground?
In my testing, I never saw my task execute again once expired (which suggests the latter?).
I was able to easily force this expiry by starting my task, backgrounding my app, then launching the iOS Camera App. My example is just using test code inspired from https://developer.apple.com/documentation/backgroundtasks/performing-long-running-tasks-on-ios-and-ipados
let request = BGContinuedProcessingTaskRequest(identifier: taskIdentifier, title: "Video Upload", subtitle: "Starting Upload")
request.strategy = .queue
BGTaskScheduler.shared.register(forTaskWithIdentifier: taskIdentifier, using: nil) { task in
guard let task = task as? BGContinuedProcessingTask else { return }
print("i am a good task")
var wasExpired = false
task.expirationHandler = {
wasExpired = true
}
let progress = task.progress
progress.totalUnitCount = 100
while !progress.isFinished && !wasExpired {
progress.completedUnitCount += 1
let formattedProgress = String(format: "%.2f", progress.fractionCompleted * 100)
task.updateTitle(task.title, subtitle: "Completed \(formattedProgress)%")
sleep(1)
}
if progress.isFinished {
print ("i was a good task")
task.setTaskCompleted(success: true)
} else {
print("i was not a good task")
task.setTaskCompleted(success: false)
}
}
try? BGTaskScheduler.shared.submit(request)
Apologies if this is clearly stated somewhere and I'm missing it.
I'm using flutter's inappwebview. All functions work on my simulator and phone, but when I submit for review, I get an answer saying infinite loading. How did it happen? I don't know what the problem is.
Topic:
App & System Services
SubTopic:
Processes & Concurrency
So i am pretty new to Xcode, but i have been using Python and other language for some while. But I am quite new to the game of view and view control. So it may be that i have over complicated this a bit - and it may be that I have some wrong understanding of the dependencies and appcontroller (that i thought would be a good idea). So here we have a main file we call it app.swift, we have a startupmanager.swift, a appcoordinator and a dependeciescontainer. But it may be that this is either a overkill - or that I am doing it wrong.
So my thought was that i had a dependeciecontainer, a appcoordinator for the views and a startupmanager that controll the initialized fetching. I have controlled the memory when i run it - checking if it is higher, lower eg - but it was first when i did my 2 days profile i saw a lot of new errors, like this: Fikser(7291,0x204e516c0) malloc: xzm: failed to initialize deferred reclamation buffer (46). and i also get macro errors, probably from the @Query in my feedview.
So my thought was that a depencecie manager and a startupmanager was a good idea together with a app coordinator.
But maybe I am wrong - maybe this is not a good idea? Or maybe I am doing some things twice? I have added a lot of prints and debugs for checking. But it seems that it starts off to heavy?
import SwiftUI
import Combine
@MainActor
class AppCoordinator: ObservableObject {
@Published var isLoggedIn: Bool = false
private var authManager: AuthenticationManager = .shared
private var cancellables = Set<AnyCancellable>()
private let startupManager: StartupManager
private let container: DependencyContainer
@Published var path = NavigationPath()
enum Screen: Hashable, Identifiable {
case profile
case activeJobs
case offers
case message
var id: Self { self }
}
init(container: DependencyContainer) {
self.container = container
self.startupManager = container.makeStartupManager()
setupObserving()
startupManager.start()
print("AppCoordinator initialized!")
}
private func setupObserving() {
authManager.$isAuthenticated
.receive(on: RunLoop.main)
.sink { [weak self] isAuthenticated in
self?.isLoggedIn = isAuthenticated
}
.store(in: &cancellables)
}
func userDidLogout() {
authManager.logout()
path.removeLast(path.count)
}
func showProfile() {
path.append(Screen.profile)
}
func showActiveJobs() {
path.append(Screen.activeJobs)
}
func showOffers() {
path.append(Screen.offers)
}
func showMessage() {
path.append(Screen.message)
}
@ViewBuilder
func viewForDestination(_ destination: Screen) -> some View {
switch destination {
case .profile:
ProfileView()
case .activeJobs:
ActiveJobsView()
case .offers:
OffersView()
case .message:
ChatView()
}
}
@ViewBuilder
func viewForJob(_ job: Job) -> some View {
PostDetailView(
job: job,
jobUserDetailsRepository: container.makeJobUserDetailsRepository()
)
}
@ViewBuilder
func viewForProfileSubview(_ destination: ProfileView.ProfileSubviews) -> some View {
switch destination{
case .personalSettings:
PersonalSettingView()
case .historicData:
HistoricDataView()
case .transactions:
TransactionView()
case .helpCenter:
HelpcenterView()
case .helpContract:
HelpContractView()
}
}
enum HomeBarDestinations: Hashable, Identifiable {
case postJob
case jobPosting
var id: Self { self }
}
@ViewBuilder
func viewForHomeBar(_ destination: HomeBarView.HomeBarDestinations) -> some View {
switch destination {
case .postJob:
PostJobView()
}
}
}
import Apollo
import FikserAPI
import SwiftData
class DependencyContainer {
static var shared: DependencyContainer!
private let modelContainer: ModelContainer
static func initialize(with modelContainer: ModelContainer) {
shared = DependencyContainer(modelContainer: modelContainer)
}
private init(modelContainer: ModelContainer) {
self.modelContainer = modelContainer
print("DependencyContainer being initialized at ")
}
@MainActor
private lazy var userData: UserData = {
return UserData(apollo: Network.shared.apollo)
}()
@MainActor
private lazy var userDetailsRepository: UserDetailsRepository = {
return UserDetailsRepository(userData: makeUserData())
}()
@MainActor
private lazy var jobData: JobData = {
return JobData(apollo: Network.shared.apollo)
}()
@MainActor
private lazy var jobRepository: JobRepository = {
return JobRepository(jobData: makeJobData(), modelContainer: modelContainer)
}()
@MainActor
func makeUserData() -> UserData {
return userData
}
@MainActor
func makeUserDetailsRepository() -> UserDetailsRepository {
return userDetailsRepository
}
@MainActor
func makeStartupManager() -> StartupManager {
return StartupManager(
userDetailsRepository: makeUserDetailsRepository(),
jobRepository: makeJobRepository(),
authManager: AuthenticationManager.shared,
lastUpdateRepository: makeLastUpdateRepository()
)
}
@MainActor
func makeJobData() -> JobData {
return jobData
}
@MainActor
func makeJobRepository() -> any JobRepositoryProtocol {
return jobRepository
}
@MainActor
private lazy var jobUserData: JobUserData = {
return JobUserData(apollo: Network.shared.apollo)
}()
@MainActor
private lazy var jobUserDetailsRepository: JobUserDetailsRepository = {
return JobUserDetailsRepository(jobUserData: makeJobUserData())
}()
@MainActor
func makeJobUserData() -> JobUserData {
return jobUserData
}
@MainActor
func makeJobUserDetailsRepository() -> JobUserDetailsRepository {
return jobUserDetailsRepository
}
@MainActor
private lazy var lastUpdateData: LastUpdateData = {
return LastUpdateData(apollo: Network.shared.apollo)
}()
@MainActor
private lazy var lastUpdateRepository: LastUpdateRepository = {
return LastUpdateRepository(lastUpdateData: makeLastUpdateData())
}()
@MainActor
func makeLastUpdateData() -> LastUpdateData {
return lastUpdateData
}
@MainActor
func makeLastUpdateRepository() -> LastUpdateRepository {
return lastUpdateRepository
}
}```
Topic:
App & System Services
SubTopic:
Processes & Concurrency
For example, let’s propose an XPC service that can connect to websites. Suppose that I want to connect to Apple.com, microsoft.com, and ibm.com. Can 3 service objects be made between the service and client? Or does the service have to return an ID for each web connection, with the client needing to specify which connection ID along with a command?
Just trying to understand the documentation.
Obviously, we can send a request to the service to return all the data at once. Can the data arrive in pieces, involving either multiple async callbacks or a Combine Publisher?
In the header for workloop.h there is this note:
A dispatch workloop is a "subclass" of dispatch_queue_t which can be passed to all APIs accepting a dispatch queue, except for functions from the dispatch_sync() family. dispatch_async_and_wait() must be used for workloop objects. Functions from the dispatch_sync() family on queues targeting a workloop are still permitted but discouraged for performance reasons.
I have a couple questions related to this. First, I'd like to better understand what the alluded-to 'performance reasons' are that cause this pattern to be discouraged in the 'queues targeting a workloop' scenario. From further interrogation of the headers, I've found these explicit callouts regarding differences in the dispatch_sync and dispatch_async_and_wait API:
dispatch_sync:
Work items submitted to a queue with dispatch_sync() do not observe certain queue attributes of that queue when invoked (such as autorelease frequency and QOS class).
dispatch_async_and_wait:
Work items submitted to a queue with dispatch_async_and_wait() observe all queue attributes of that queue when invoked (inluding [sic] autorelease frequency or QOS class).
Additionally, dispatch_async_and_wait has a section of the headers devoted to 'Differences with dispatch_sync()', though I can't say I entirely follow the distinctions it attempts to draw.
Based on that, my best guess is that the 'performance reasons' are something about either QoS not being properly respected/observed or some thread context switching differences that can degrade performance, but I would appreciate insight from someone with more domain knowledge.
My second question is a bit more general – taking a step back, why exactly do these two API exist? It's not clear to me from the existing documentation I've found why I would/should prefer dispatch_sync over dispatch_async_and_wait (other than the aforementioned callout noting the former is unsupported on workloops). What is the motivation for preserving both these API vs deprecating dispatch_sync in favor of dispatch_async_and_wait (or functionally subsuming one with the other)?
Credit to Luna for originally posing/inspiring these questions.
iOS BGProcessingTask + Background Upload Not Executing Reliably on TestFlight (Works in Debug)
Description:
We are facing an issue with BGTaskScheduler and BGProcessingTask when trying to perform a background audio-upload flow on iOS. The behavior is inconsistent between Debug builds and TestFlight (Release) builds.
Summary of the Problem
Our application records long audio files (up to 1 hour) and triggers a background upload using:
BGTaskScheduler
BGProcessingTaskRequest
Background URLSession (background with identifier)
URLSession background upload task + AppDelegate.handleEventsForBackgroundURLSession
In Debug mode (Xcode → Run on device), everything works as expected:
BGProcessingTask executes
handleEventsForBackgroundURLSession fires
Background URLSession continues uploads reliably
Long audio files successfully upload even when the app is in background or terminated
However, in TestFlight / Release mode, the system does not reliably launch the BGProcessingTask or Background URLSession events.
Technical Details
We explicitly register BGTaskScheduler:
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "example.background.process",
using: nil
) { task in
self.handleBackgroundProcessing(task: task as! BGProcessingTask)
}
We schedule it using:
let request = BGProcessingTaskRequest(identifier: "example.background.process")
request.requiresNetworkConnectivity = true
request.requiresExternalPower = false
try BGTaskScheduler.shared.submit(request)
We also use Background URLSession:
let config = URLSessionConfiguration.background(withIdentifier: sessionId)
config.sessionSendsLaunchEvents = true
config.isDiscretionary = false
AppDelegate.handleEventsForBackgroundURLSession is implemented correctly and works in Debug.
Issue Observed (TestFlight Only)
In TestFlight builds:
BGProcessingTask rarely triggers, or the system marks it as NO LONGER RUNNING.
Background upload tasks sometimes never start or complete.
No logs appear from our BGProcessingTask handler.
system logs show messages like:
NO LONGER RUNNING bgProcessing-example.background.process
Tasks running in group [com.apple.dasd.defaultNetwork] are 1!
This occurs most frequently for large audio uploads (30–60 minutes), while small files behave normally.
What We Have Verified
Proper Info.plist values:
Permitted background modes: processing, audio, fetch
BGTaskSchedulerPermittedIdentifiers contains our identifier
BGProcessingTask is being submitted successfully (no errors)
App has microphone permission + background audio works
Device plugged/unplugged doesn’t change outcome
Key Question for Apple
We need clarification on:
Why BGProcessingTask behave differently between Debug and TestFlight builds?
Are there additional restrictions or heuristics (related to file size, CPU usage, runtime, network load, or power constraints) that cause BGProcessingTask to be throttled or skipped in Release/TestFlight?
How can we guarantee a background upload continues reliably for large files (100MB–500MB) on TestFlight and App Store builds?
Is there an Apple-recommended pattern to combine BGProcessingTask + Background URLSession for long-running uploads?
Expected Result
Background uploads should continue reliably for long audio files (>30 minutes) when the app goes to background or is terminated, in the same way they currently function in Debug builds.
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
iOS
Background Tasks
Foundation
CFNetwork
As far as I understand, the main thread has a run loop.
When an iOS app launches, the process must keep the run loop running to stay alive.
Does that mean the main thread is the very first thread created when the process starts?
Are there any plans to add RBI support (the sending keyword) to the OSAllocatedLock interface? So it could be used with non-sendable objects without surrendering to the unchecked API
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
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
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
Background Tasks
Core Bluetooth
Regarding the Background Assets capability on iOS:
In the install scenario, resources defined as the "install" type are incorporated into the App Store download progress. Do resources of the "update" type in the update scenario also get incorporated into the App Store download progress in the same way?
If an exception occurs during the download of install-type resources and the download cannot proceed further, will the system no longer actively block users from launching the app and instead enable the launch button?
Currently, if a user has enabled automatic updates on their device, after the app is updated and released on the App Store, will the Background Assets download start immediately once the automatic update completes? Or does Background Assets have its own built-in scheduling logic that prevents it from running concurrently with the automatic update?
I'm try to monitor all processes by ES client. But I found the process name is different from the Activity Monitor displayed. As shown in the picture below, there are ShareSheetUI(Pages) and ShareSheetUI(Finder) processes in Activity Monitor, but I can only get the same name ShareSheetUI, I thought of many ways to display the name in parentheses, but nothing worked, so there is a way to display the process name like Activity Monitor?
Hi,
I have requirement in iOS where application needs to run in the background
It can be a simple hello world program running in the background.
could you shed some light on what is the expected behaviour and is it allowed in iOS.
I have implemented a XPC server using C APIs. I want to write unit tests for it.
I came across the following links that use Swift APIs-
Testing and Debugging XPC Code With an Anonymous Listener
TN3113
I have tried to write anonymous listener code and the client code in the same file, using C APIs-
#include <unistd.h>
#include <syslog.h>
#include <pthread.h>
#include <stdio.h>
#include <xpc/xpc.h>
#include <xpc/connection.h>
#include <CoreFoundation/CoreFoundation.h>
static void Anon_Client_Connection_Handler(xpc_connection_t connection, xpc_object_t clientMessage)
{
const char *description = xpc_copy_description(clientMessage);
printf("Event received - %s\n", description);
free((void *)description);
xpc_type_t type = xpc_get_type(clientMessage);
if (type == XPC_TYPE_ERROR)
{
if (clientMessage == XPC_ERROR_CONNECTION_INVALID)
printf("Client_Connection_Handler received invalid connection n");
else if (clientMessage == XPC_ERROR_TERMINATION_IMMINENT)
printf("Client_Connection_Handler received termination notice n");
}
else
{
const char *clientMsg = xpc_dictionary_get_string(clientMessage, "message");
printf("Received from client: %s ", clientMsg);
}
}
static void Anon_Listener_Connection_Handler(xpc_connection_t connection)
{
printf("Anon_Listener_Connection_Handler called, setting up event handler \n");
xpc_connection_set_event_handler(connection, ^(xpc_object_t clientMessage) {
printf("Processing the connection! \n");
Anon_Client_Connection_Handler(connection, clientMessage);
});
xpc_connection_resume(connection);
}
int main(int argc, const char *argv[])
{
xpc_connection_t anon_listener = xpc_connection_create(NULL, NULL);
xpc_connection_set_event_handler(anon_listener, ^(xpc_object_t clientConnection) {
printf("Client tried to connect \n");
Anon_Listener_Connection_Handler(clientConnection);
});
xpc_connection_resume(anon_listener);
printf("\nINFO Anonymous connection resumed");
xpc_object_t anon_endpoint = xpc_endpoint_create(anon_listener);
xpc_connection_t clientConnection = xpc_connection_create_from_endpoint(anon_endpoint);
xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_string(message, "message", "client's message");
xpc_connection_send_message_with_reply(clientConnection, message, dispatch_get_main_queue(), ^(xpc_object_t event) {
printf("\nINFO inside reply");
const char *description = xpc_copy_description(event);
printf("\nINFO %s",description);
free((void *)description);
});
xpc_release(message);
xpc_release(anon_listener);
printf("\nINFO Releasing listener");
xpc_release(anon_endpoint);
printf("\nINFO Releasing endpoint");
// dispatch_main();
return 0;
}
and this is the output I get
INFO Anonymous connection resumed
INFO Releasing listener
INFO Releasing endpoint
I am not able to connect to the client and exchange messages. Where am I going wrong?
Hi,
I'm trying to create a launch daemon that uses XPC to receive requests from an unprivileged app. Ultimately both components will be written in Go. For now I'm trying to write a PoC in Objective-C to make sure I get everything right, so I'm compiling / signing from the CLI, and writing plist files by hand -- I'm not using XCode.
My current daemon code is pretty much the same as the boilerplate code that XCode generates when creating a new 'XPC Service':
#import <stdio.h>
#include <xpc/xpc.h>
int main(int argc, char *argv[]) {
xpc_rich_error_t error;
dispatch_queue_t queue = dispatch_queue_create("com.foobar.daemon", DISPATCH_QUEUE_SERIAL);
xpc_listener_t listener = xpc_listener_create(
"com.foobar.daemon",
queue,
XPC_LISTENER_CREATE_NONE,
^(xpc_session_t _Nonnull peer) {
xpc_session_set_incoming_message_handler(peer, ^(xpc_object_t _Nonnull message) {
int64_t firstNumber = xpc_dictionary_get_int64(message, "firstNumber");
int64_t secondNumber = xpc_dictionary_get_int64(message, "secondNumber");
// Create a reply and send it back to the client.
xpc_object_t reply = xpc_dictionary_create_reply(message);
xpc_dictionary_set_int64(reply, "result", firstNumber + secondNumber);
xpc_rich_error_t replyError = xpc_session_send_message(peer, reply);
if (replyError) {
printf("Reply failed, error: %s", xpc_rich_error_copy_description(replyError));
}
});
},
&error);
if (error != NULL) {
printf("ERROR: %s\n", xpc_rich_error_copy_description(error));
exit(1);
}
printf("Created listener: %s", xpc_listener_copy_description(listener));
// Resuming the serviceListener starts this service. This method does not return.
dispatch_main();
return 0;
}
I'm compiling, signing and installing my daemon with the following commands:
build_foobar() {
clang -Wall -x objective-c -o com.foobar.daemon poc/main.m
codesign --force --verify --verbose --options=runtime \
--identifier="com.foobar.daemon" \
--sign="Mac Developer: Albin Kerouanton (XYZ)" \
--entitlements=poc/entitlements.plist \
com.foobar.daemon
}
install_foobar() {
sudo cp com.foobar.daemon /Library/PrivilegedHelperTools/com.foobar.daemon
sudo cp poc/com.foobar.daemon.plist /Library/LaunchDaemons/com.foobar.daemon.plist
sudo launchctl bootout system/com.foobar.daemon || true
sudo launchctl bootstrap system /Library/LaunchDaemons/com.foobar.daemon.plist
}
Here's the content of my entitlements.plist file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.application-identifier</key>
<string>ABCD.com.foobar.daemon</string>
</dict>
</plist>
And finally, here's my launchd plist file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.foobar.daemon</string>
<key>Program</key>
<string>/Library/PrivilegedHelperTools/com.foobar.daemon</string>
<key>ProgramArguments</key>
<array>
<string>/Library/PrivilegedHelperTools/com.foobar.daemon</string>
</array>
<key>RunAtLoad</key>
<false/>
<key>StandardOutPath</key>
<string>/tmp/com.foobar.daemon.out.log</string>
<key>StandardErrorPath</key>
<string>/tmp/com.foobar.daemon.err.log</string>
<key>Debug</key>
<true/>
</dict>
</plist>
Whenever I start my service using sudo launchctl start com.foobar.daemon, it exits with the following error message:
ERROR: Unable to activate listener: failed at listener activation with error 1 - Operation not permitted
System logs don't show anything interesting -- they're just repeating the same error message. I tried to add / remove some properties from both the entitlement and the launchd plist file but to no avail.
Any idea what's going wrong?