XPC, Swift, ObjC, and arrays

I create a protocol that had, among other things:

@objc func setList(_: [MyType], withReply: @escaping (Error?) -> Void)

The daemon part is in Swift, while the calling part is in Objective-C. Because why not? (Actually, because the calling part has to deal with C++ code, so that's ObjC++; however, I wanted the stronger typing and runtime checking for the daemon part, so I wrote it in Swift.) The ObjC part uses NSArray<MyType*>.

I set up an NSXPCConnection link, and create a (synchronous) proxy with the right protocol name. But when I try to do the XPC setList call, I get an error. I assume that's because it doesn't like the signature. (Surely this is logged somewhere? I couldn't find it, if so. 😩) But... if I have a signature of @objc func addItem(_: MyType, withReply: @escaping (Error?) -> Void), then it works. So I assume it's the array. (Oh, I've also tried it without the @objc; the protocol itself is defined as @objc.)

I've tried changing to protocol signature to using NSArray, but same thing.

Accepted Reply

Objective-C array elements can be of any object type, and indeed different types of objects, which can come as a shock in the XPC world where the client can send you an array whose elements are of an unexpected type. To avoid any resulting security vulnerabilities you have to tell NSXPCConnection the type of elements to expect. Do this using the setClasses(_:for:argumentIndex:ofReply:) method on NSXPCInterface.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Replies

Objective-C array elements can be of any object type, and indeed different types of objects, which can come as a shock in the XPC world where the client can send you an array whose elements are of an unexpected type. To avoid any resulting security vulnerabilities you have to tell NSXPCConnection the type of elements to expect. Do this using the setClasses(_:for:argumentIndex:ofReply:) method on NSXPCInterface.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

That gets done in the part that will receive the data? And if I call that, I have to also specify String/NSString, Int/NSInt, etc., in addition to my custom classes?

I will be googling for examples later, since I have just woken up, but thanks as usual. 😄

I am in fact failing to find examples of setClasses(_:for:argumentIndex:ofReply:) for Swift. The big thing I'm running into is that the .class member of a class is not hashable. Specifically: note: only concrete types such as structs, enums and classes can conform to protocols

And, ok, I got that solved.

        let exportedInterface = NSXPCInterface(with: MyProtocol.self)
        let allowedClasses = exportedInterface.classes(for: #selector(setList(_:withReply:)), argumentIndex:0, ofReply:false)
        let newSet = allowedClasses.union(NSSet(object: MyClass.self) as! Set<AnyHashable>)
        exportedInterface.setClasses(newSet, for:#selector(setList(_:withReply:)), argumentIndex:0, ofReply:false)
        newConnection.exportedInterface = exportedInterface

(I have to do the same in ObjC for the "user" side, because it can get a list of MyClass in a reply, but ObjC is a lot easier, and better documented as well, for this.)

Thanks 😄