I have a project that leverages XPC and has interoperability between Swift and Objective-C++. I am presently getting a compile-time error in one of our unit test targets, of "Argument passed to call that takes no arguments" on the following code:
let interface = NSXPCInterface(with: XPCServiceDelegate.self)
My XPCServiceDelegate protocol is defined as:
@objc(XPCServiceDelegate) public protocol XPCServiceDelegate {
//...
}
For the longest time, this code has compiled successfully, and it has not recently changed. There are two confusing things about this error. The first is that I have a different build scheme that will compile correctly other code with the same structure. The other is that I have team members that are able to compile my failing scheme successfully on the same XCode version, OSVersion, and branch of our repository.
I've attempted numerous things to try to get this code to compile, but I've run out of ideas.
Here's what I've tried:
Clean build both on XCode 16.4 and XCode 26 Beta
Delete DerivedData and rebuild on XCode 16.4 and XCode 26 Beta
Delete and re-clone our git repository
Uninstall and reinstall XCode
Attempt to locate cached data for XCode and clear it out. (I'm not sure if I got everything that exists on the system for this.)
Ensure all OS and XCode updates have been applied.
The interface specification for NSXPCInterface clearly has an initializer with one arguement for the delegate protocol, so I don't know why the compiler would fail for this. Is there some kind of forward declaration or shadowing of NSXPCInterface? Do you have any ideas on what I could try next?
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Created
I'm attempting to root-cause analyze issues I've been having with launch daemons controlled by launchd.
My product has a single privileged LaunchDaemon running in the system domain (supposed to at least). I was looking at a client's machine that, for some reason, was running the daemon process both in the system domain and the gui/ domain for the logged in user. When I would kill both processes on the client machine, both of them would start right back up in their respective domains. The associated plist for this daemon process only exists in /Library/LaunchDaemons.
My question is, how can this be possible that launchd would spin up multiple privileged daemons, but one of them running in the gui/ domain? How can this be prevented from happening?
Some context/details:
It was observed on the client's machine that the daemon service was previously disabled. The service was manually re-enabled using:
launchctl enable system/<nameOfDaemon>
sudo launchctl enable system/<nameOfDaemon>
The daemon was reloaded by using:
sudo launchctl load /Library/LaunchDaemons/<plist of daemon process>
I am attempting to understand the expected behavior of several points related to XPC. Mainly, I have the following questions:
Should I expect that NSXPCConnection.remoteObjectProxyWithErrorHandler to call the error handler if the associated NSXPCListenerDelegate returns false in its listener function?
Does the error handler get called immediately if the remoteObjectProxyWithErrorHandler function fails?
What does remoteObjectProxy return if an actual proxy object is never exported on the service-side (in the listener function)?
Should I expect that NSXPCConnection.invalidate() and/or the connection's invalidationHandler to be called when the associated NSXPCListenerDelegate returns false in its listener function?
According to the listener documentation below, it appears the listener function is supposed to invalidate the connection; however, I am not seeing this be the case.
https://developer.apple.com/documentation/foundation/nsxpclistenerdelegate/1410381-listener
To reject the connect, return a value of false. This causes the connection object to be invalidated.
I have written a test that exhibits the fact when the listener function returns false, rather than the invalidationHandler being called, the interruptionHandler is called.
import XCTest
final class InvalidateTest: XCTestCase {
func testConnectionIsInvalidatedOnListenerRejection() {
//set up anonymous XPC Listener
let listener = NSXPCListener.anonymous()
let listenerDelegate = MockListenerDelegate()
listener.delegate = listenerDelegate
listener.resume()
//establish connection to service
let clientConnection = MockConnection(listenerEndpoint: listener.endpoint)
var interruptHandlerCalled = false
var invalidateHandlerCalled = false
let interruptionHandler = { interruptHandlerCalled = true }
let invalidationHandler = { invalidateHandlerCalled = true }
clientConnection.interruptionHandler = interruptionHandler
clientConnection.invalidationHandler = invalidationHandler
clientConnection.remoteObjectInterface = NSXPCInterface(with: XPCServDelegate.self)
clientConnection.exportedInterface = NSXPCInterface(with: XPCCliDelegate.self)
clientConnection.exportedObject = XPCCDelegate()
clientConnection.resume()
// get the proxy delegate to make the XPC pulse call
guard let proxy = clientConnection.remoteObjectProxy() as? XPCServDelegate else {
XCTAssert(false, "Unable to get proxy object")
return
}
// why is it not failing to get a proxy object in this case?
// make the pulse call
var replyCalled = false
let semaphore = DispatchSemaphore(value: 0)
proxy.pulse(reply: {
replyCalled = true
semaphore.signal()
})
let waitResult = semaphore.wait(timeout: .now() + 1)
XCTAssertEqual(waitResult, .timedOut)
XCTAssertFalse(replyCalled)
// why do these assertions fail?
XCTAssertTrue(clientConnection.invalidateCalled)
XCTAssertTrue(invalidateHandlerCalled)
XCTAssertFalse(interruptHandlerCalled)
}
}
@objc public protocol XPCServDelegate {
func pulse(reply: @escaping () -> Void)
}
class XPCSDelegate : XPCServDelegate {
func pulse(reply: @escaping () -> Void) {
reply()
}
}
@objc public protocol XPCCliDelegate {}
class XPCCDelegate : XPCCliDelegate {}
fileprivate class MockConnection : NSXPCConnection {
var invalidateCalled = false
override func invalidate() {
invalidateCalled = true
super.invalidate()
}
}
fileprivate class MockListenerDelegate : NSObject, NSXPCListenerDelegate {
func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
return false
}
}
I am attempting to dynamically clone a class object in Swift using reflection and the setValue function. This class contains an enum property that is backed with an Int, which is causing this dynamic reflection to crash:
@objc enum Status : Int {
case green
case yellow
case red
}
@objc class State : NSObject {
@objc var status : Status
init(_ status: Status) {
self.status = status
}
}
func testReflectiveClone() {
let state1 = State(.green)
let state2 = State(.yellow)
let state1Mirror = Mirror(reflecting: state1)
for property in state1Mirror.children.enumerated() {
let label = property.element.label
state2.setValue(property.element.value, forKey: label!) //crashes here
}
}
This test function is throwing the following error in XCode:
-[__SwiftValue longLongValue]: unrecognized selector sent to instance 0x600001e5a340 (NSInvalidArgumentException)
Is it even possible to dynamically set enum values? What modification would I need to make to get this to work?
I have an XCode Workspace that builds a product as a universal binary for Intel and Apple Silicon.
On an Intel-based machine, I am attempting to import a Swift Package dependency by accessing a private git repository.
When I attempt to import my dependency into my code and build, I'm getting the following error:
Could not find module '<swift package name>' for target 'arm64-apple-macos'; found: x86_64, x86_64-apple-macos
The project has Standard Architectures set for the ARCHS build setting and ONLY_ACTIVE_ARCH = NO. When I switch ONLY_ACTIVE_ARCH = YES, it builds fine.
Is there a way to instruct the compiler to build the swift package dependency as universal and resolve this issue?
Can a Launch Daemon Communicate with Concurrent XPC Connections Configured with Different Interfaces
I have a use case in which I have a launch daemon (as the XPC service) than needs to communicate with two XPC clients. Each of these clients has different functional cases for communication that do not overlap. Therefore, the NSXPCInterface for client A would be configured with a different protocol than the NSXPCInterface that would be configured for client B. Client A and Client B do not need to talk to each other; they each just need to communicate with the daemon.
I am confused how to appropriately set up the NSXPCListener and NSXPCListenerDelegate on the daemon to support NSXPCConnections with proxy objects that adhere to varying interfaces to support these two clients.
Is there a way for a single NSXPCListener (and associated delegate) to listen for connections requiring the exportedInterface to be different?
Is there a way to send data through the NSXPCConnection that will allow the NSXPCListenerDelegate to conditionally determine which exported interface and object to configure?
One idea I had was to have the daemon contain two NSXPCListeners. Each listener would be responsible for connections coming from the respective clients.
Will this option work? If so, it is the advisable approach?
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
XPC
Inter-process communication
I have small integration test that is confirming behavior in XPC communications (leveraging NSXPCConnection) between a dummy XPC service and an XPC client. My test is flaky, which is indicating to me that I don't fully understand the nature of connections between services and clients, and I was hoping to get clarity as to what's happening in my test.
My test involves the following steps.
XCode Pre-Action: Load the plist for a dummy XPC service into launchctl.
Create 2 XPC client objects (in the same client process), each with their own NSXPCConnection to the dummy service, and connect.
Tell client 1 to disconnect, which calls NSXPCConnection.invalidate()
Using client 2, send a message to the same dummy XPC service over it's own NSXPCConnection object.
Wait for the echo response from the dummy XPC service
XCode Post-Action: Unload the plist for the dummy XPC service from launchctl
func testMultipleConnections() {
let delegate1 = MockClientDelegate()
let delegate2 = MockClientDelegate()
let client1 = XPCMessagingClientFacade(withServiceName: serviceName, andXPCErrorHandler: {error in })
let client2 = XPCMessagingClientFacade(withServiceName: serviceName, andXPCErrorHandler: {error in })
client1.processMessageDelegate = delegate1
client2.processMessageDelegate = delegate2
_ = client1.connect()
_ = client2.connect()
_ = client1.disconnect()
delegate2.expectation = XCTestExpectation(description: "Message received from echo service")
_ = client2.sendMessage(ofMessageType: eMTAction_Uninstall, withData: ["dummy": "data"])
wait(for: [delegate2.expectation!], timeout: timeout)
}
This test sometimes succeeds and sometimes fails. Sometimes, the test expectation at the bottom is fulfilled, and sometimes the timeout is hit. I have tested with excessively long timeouts to rule-out processing-time as as factor.
I am suspecting that calling invalidate() on one NSXPCConnection object is somehow causing a separate connection between the same client and service process to also be invalidated.
Is it even a valid use-case to have multiple NSXPCConnection objects between a single XPC Service and XPC Client?
When NSXPCConnection.invalidate() is called, does it inherently mean nothing can connect and communicate anymore, or is it just for that specific connection?
When invalidate() is called, what does launchctl do to manage that service? Does it shut it down and allow subsequent connection attempts to spool the service up again? Does it prevent from any connections from ever being made again?
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
XPC
Service Management
Inter-process communication