mutualmobile / MMWormhole

Message passing between iOS apps and extensions.
MIT License
3.94k stars 310 forks source link

Crash when unarchiving object #39

Closed edwardmp closed 9 years ago

edwardmp commented 9 years ago

First of all thank you for publishing this framework, it should be very useful for communicating with Apple Watch extensions.

I am trying to receive notifications regarding Core Data updates, as suggested in this thread: http://stackoverflow.com/questions/27791594/watchkit-core-data-sync-up/29475315#29475315

As soon as MMWormhole sends a message from iOS, the receiving Watch app crashes on the following line: id messageObject = [NSKeyedUnarchiver unarchiveObjectWithData:data];

It has something to with unarchiving an object (the Core Data ManagedObject I assume)

Full crash report:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSManagedObject initWithCoder:]: unrecognized selector sent to instance 0x6180000b4820'
*** First throw call stack:
(
    0   CoreFoundation                      0x000000010f346c65 __exceptionPreprocess + 165
    1   libobjc.A.dylib                     0x000000010efdfbb7 objc_exception_throw + 45
    2   CoreFoundation                      0x000000010f34e0ad -[NSObject(NSObject) doesNotRecognizeSelector:] + 205
    3   CoreFoundation                      0x000000010f2a413c ___forwarding___ + 988
    4   CoreFoundation                      0x000000010f2a3cd8 _CF_forwarding_prep_0 + 120
    5   Foundation                          0x000000010eb38584 _decodeObjectBinary + 2872
    6   Foundation                          0x000000010eb39587 -[NSKeyedUnarchiver _decodeArrayOfObjectsForKey:] + 1832
    7   Foundation                          0x000000010eb38b51 -[NSSet(NSSet) initWithCoder:] + 203
    8   Foundation                          0x000000010eb38584 _decodeObjectBinary + 2872
    9   Foundation                          0x000000010eb39587 -[NSKeyedUnarchiver _decodeArrayOfObjectsForKey:] + 1832
    10  Foundation                          0x000000010eb584aa -[NSDictionary(NSDictionary) initWithCoder:] + 203
    11  Foundation                          0x000000010eb38584 _decodeObjectBinary + 2872
    12  Foundation                          0x000000010eb378da _decodeObject + 278
    13  Foundation                          0x000000010ec05a61 -[NSNotification initWithCoder:] + 113
    14  Foundation                          0x000000010eb38584 _decodeObjectBinary + 2872
    15  Foundation                          0x000000010eb378da _decodeObject + 278
    16  Foundation                          0x000000010eb5a971 +[NSKeyedUnarchiver unarchiveObjectWithData:] + 89
    17  My WatchKit Extension               0x000000010eaa4f71 -[MMWormhole messageObjectFromFileWithIdentifier:] + 225
    18  My WatchKit Extension               0x000000010eaa53f2 -[MMWormhole didReceiveMessageNotification:] + 194
    19  CoreFoundation                      0x000000010f31654c __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 12
    20  CoreFoundation                      0x000000010f214a04 _CFXNotificationPost + 2484
    21  Foundation                          0x000000010eb25968 -[NSNotificationCenter postNotificationName:object:userInfo:] + 66
    22  My WatchKit Extension               0x000000010eaa5270 wormholeNotificationCallback + 208
    23  CoreFoundation                      0x000000010f31654c __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 12
    24  CoreFoundation                      0x000000010f2cf86e ____CFXNotificationPostToken_block_invoke + 142
    25  CoreFoundation                      0x000000010f27a41c __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 12
    26  CoreFoundation                      0x000000010f270165 __CFRunLoopDoBlocks + 341
    27  CoreFoundation                      0x000000010f26ff25 __CFRunLoopRun + 2389
    28  CoreFoundation                      0x000000010f26f366 CFRunLoopRunSpecific + 470
    29  Foundation                          0x000000010eb84f92 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 275
    30  Foundation                          0x000000010ec10080 -[NSRunLoop(NSRunLoop) run] + 74
    31  libxpc.dylib                        0x0000000111c21aa9 _xpc_objc_main + 386
    32  libxpc.dylib                        0x0000000111c23e91 xpc_main + 185
    33  Foundation                          0x000000010ecfcee1 service_connection_handler + 0
    34  PlugInKit                           0x00000001196c8a82 -[PKService run] + 521
    35  WatchKit                            0x0000000110ad8c44 main + 126
    36  libdyld.dylib                       0x000000011196b145 start + 1
    37  ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
kevin-zqw commented 9 years ago

Your NSManagedObject subclass need conform to NSCoding protocol, which means implement:

- (void)encodeWithCoder:(NSCoder *)aCoder;
- (id)initWithCoder:(NSCoder *)aDecoder;
edwardmp commented 9 years ago

Thanks for the reply. Yes that is what I did (the StackOverflow post mentioned that).

Code I used for this:

- (id)initWithCoder:(NSCoder *)decoder {
    NSManagedObjectContext *context = [[CoreDataManager sharedManager] managedObjectContext]; // use your NSManagedObjectContext
    NSPersistentStoreCoordinator *coordinator = [context persistentStoreCoordinator]; //use your NSPersistentStoreCoordinator
    NSURL *url = (NSURL *)[decoder decodeObjectForKey:@"URIRepresentation"];
    NSManagedObjectID *managedObjectID = [coordinator managedObjectIDForURIRepresentation:url];
    self = (Flight* )[context existingObjectWithID:managedObjectID error:nil];
    return self;
}

- (void)encodeWithCoder:(NSCoder *)encoder {
    [encoder encodeObject:[[self objectID] URIRepresentation] forKey:@"URIRepresentation"];
}
cnstoll commented 9 years ago

Yeah, that's more or less the best you can do. As a word of warning, I'd be careful using a generic MOC that way. That can work, but it can also be an anti-pattern as far as Core Data is concerned. In general, I'd recommend simply passing the Managed Object ID representations through as plain strings and having a different object on the other side that is capable of fetching managed objects for those IDs with a specific context. It's more code, but it's a little more deliberate, which with Core Data can be a good thing in the long run. Just food for thought.