Serializing class across watchOS and iOS framework

I will try to explain my situation:


I have an app that has two frameworks: one for iOS and one for watchOS. There are shared source files between the two as well as target specific source files.


I will name them: MyAppFrameworkKit and MyAppFrameworkKitLite with the "lite" one being on watchOS.


To allow communication between the two, I have created a MyAppEventMessage class that is serializable with NSSecureCoding.


However, and this makes sense now, when I serialize this object and send it to the iOS side, there is a NSKeyedUnarchiver crash:


*** Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: '*** -[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (MyAppFrameworkKitLite.MyAppEventMessage) for key (root); the class may be defined in source code or a library that is not linked'


Now this makes sense to me because MyAppFrameworkKitLite is *not* linked from the iOS side. The iOS version is linked to the MyAppFrameworkKit. Even though they share the same MyAppEventMessage class in their frameworks, the module names are obviously not the same.


How are developers serializing information between their iOS and watchOS apps? As far as I can tell, it is not possible to have both targets link against a framework with the same module name while also targeting iOS or watchOS.


I would hate to have to include these files in the source of my apps instead of their respective framework as I like the separation.


Any help or clarifiaction is much appreciated

Accepted Reply

Theoretically, if you give both versions of your class the same obj-C name using @objc(InsertClassNameHere), it should prevent it from being name-spaced and it should appear as the same class to objC classes like NSKeyedUnarchiver.


https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-ID53

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 you migrate 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.


I'm saying theoretically because I haven't tried it myself, and the last time I suggested doing this the op responded that it didn't work for them (sending an archived object via an XPC service), although based on the documentation it should have.

Replies

Theoretically, if you give both versions of your class the same obj-C name using @objc(InsertClassNameHere), it should prevent it from being name-spaced and it should appear as the same class to objC classes like NSKeyedUnarchiver.


https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-ID53

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 you migrate 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.


I'm saying theoretically because I haven't tried it myself, and the last time I suggested doing this the op responded that it didn't work for them (sending an archived object via an XPC service), although based on the documentation it should have.

I should also add that the classes of any properties within that MyAppEventMessage class would also need to be similarly objc(NAME) renamed to remove namespacing too, unless all the properties are things that are being converted to NSString/NSNumber, etc.

You were so right with this!


I tried to follow the pattern in the Lister app, where both frameworks were named the same (ListerKit but with my own app name).


However, this BLEW up when I tried archiving my app as it complained about iOS trying to link to arm7k and watchOS trying to link to arm7s.


I renamed my module and used @objc for classes that conform to NSSecureCoding and it worked when I archived.


I wish this were simpler but this definitely works and fixes the issue.


Thanks a ton!