ioscore-datanscoder

Can NSManagedObject conform to NSCoding


I need to transfer a single object across device. Right now I am converting my NSManagedObject to a dictionary , archiving it and sending as NSData. Upon receiving I am unarchiving it. But I would really like to transfer the NSManagedObject itself by archiving and unarchiving instead of creating an intermediate data object.

@interface Test : NSManagedObject<NSCoding>
@property (nonatomic, retain) NSString * title;
@end

@implementation Test
@dynamic title;

- (id)initWithCoder:(NSCoder *)coder {
    self = [super init];
    if (self) {
        self.title = [coder decodeObjectForKey:@"title"]; //<CRASH
    }
    return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
    [coder encodeObject:self.title forKey:@"title"];
}
@end


NSData *archivedObjects = [NSKeyedArchiver archivedDataWithRootObject:testObj];
NSData *objectsData = archivedObjects;
if ([objectsData length] > 0) {
    NSArray *objects = [NSKeyedUnarchiver unarchiveObjectWithData:objectsData];
}

The problem with the above code is. It crashes at self.title in initWithCoder saying unrecognized selector sent to instance.


Solution

  • This snippet below should do the trick. The main difference is to call super initWithEntity:insertIntoManagedObjectContext:

    - (id)initWithCoder:(NSCoder *)aDecoder {
       NSEntityDescription *entity = [NSEntityDescription entityForName:@"Test" inManagedObjectContext:<YourContext>];
    
       self = [super initWithEntity:entity insertIntoManagedObjectContext:nil];
       NSArray * attributeNameArray = [[NSArray alloc] initWithArray:self.entity.attributesByName.allKeys];
    
       for (NSString * attributeName in attributeNameArray) {
            [self setValue:[aDecoder decodeObjectForKey:attributeName] forKey:attributeName];
       }
       return self;
    }
    

    Above snippet will handle only the attributes, no relationships. Dealing with relationships as NSManagedObjectID using NSCoding is horrible. If you do need to bring relationships across consider introducing an extra attribute to match the two (or many) entities when decoding.

    how to obtain <YourContext>

    (based on a now unavailable post by Sam Soffes, code taken from https://gist.github.com/soffes/317794#file-ssmanagedobject-m)

    + (NSManagedObjectContext *)mainContext {
         AppDelegate *appDelegate = [AppDelegate sharedAppDelegate];
    return [appDelegate managedObjectContext];
    }
    

    Note: replace <YourContext> in the first snippet with mainContext