XPC Connection Not Invalidated on NSXPCListenerDelegate Denial

I am attempting to understand the expected behavior of several points related to XPC. Mainly, I have the following questions:

  1. Should I expect that NSXPCConnection.remoteObjectProxyWithErrorHandler to call the error handler if the associated NSXPCListenerDelegate returns false in its listener function?
  2. Does the error handler get called immediately if the remoteObjectProxyWithErrorHandler function fails?
  3. What does remoteObjectProxy return if an actual proxy object is never exported on the service-side (in the listener function)?
  4. 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
  }
}
XPC Connection Not Invalidated on NSXPCListenerDelegate Denial
 
 
Q