Apr 16, 2018

Swift Namespacing, Archiving, and XPC

This is admittedly esteric, but I found no answer when I ran into this problem, so I'm writing it up here. If you're having trouble passing your custom Swift object across an XPC connection, read on.

Swift allows you some control over how your Swift class will be imported into Objective-C. There is an @objc attribute that controls the name that is presented in Objective-C. From Using Swift From Cocoa and Objective-C:

You can use the @objc(name) attribute to change the name of a class, property, method, enumeration type, or enumeration case declaration in your interface as it's exposed to Objective-C code.

But keep reading, and you'll find this nugget about how that attribute affects namespacing:

When you use the @objc(name) attribute on a Swift class, the class is made available in Objective-C without any namespacing. As a result, this attribute can also be useful when migrating an archivable Objective-C class to Swift. Because archived objects store the name of their class in the archive, you should use the @objc(name) attribute to specify the same name as your Objective-C class so that older archives can be unarchived by your new Swift class.

In other words, if your class implements NSCoding or NSSecureCoding, you'll probably want to declare your class like this:

@objc(MyClass) class MyClass { /* ... */ }

even though this might appear redundant. This includes the case of using NSSecureCoding to pass custom objects across an XPC connection. If your custom object is imported from a library, this may not be necessary. But if your custom object is included in two targets (the XPC helper target and the host application target), the object will appear as two different classes because of how Swift namespacing works on the basis of the target.