1
votes

I've seen others asking about how to use an NSManagedObject outside of the managedObjectContext. Seems like everyone says you should not do this, but I can't find information on what to do instead.

I'm essentially trying to do two different things with the data that is set on my NSManagedObject. I want to save it to the persistentStore, and I want to send it to a remote server. My idea was to alloc/init an instance of my NSManagedObject, populate it's properties, then pass that to an function where those properties would be transferred to a properly instantiated NSManagedObject, and then to pass it to another function that would be responsible for sending the data to a server.

In code: (Event is a subclass of NSManagedObject)

// in my view controller
Event *event = [Event alloc] init];
event.propertyA = @"foo";
event.propertyB = @"bar";

[self logEvent:event];
[self sendEvent:event];

-----------------------------------

// method in view controller 
- (void)logEvent(Event *)event {
    // my thought was to take the event that I manually created, and use it to
    // set the properties on the Event object in the managedObjectContext.

    Event *eventEntity = [NSEntityDescription insertNewObjectForEntityForName:@"Event" inManagedObjectContext:self.managedObjectContext];

    eventEntity.propertyA = event.propertyA;
    eventEntity.propertyB = event.propertyB;
    ...
    [self.managedObjectContext save:&error];
}

- (void) sendEvent:(Event *)event {
    // send exact same event properties to remote server
}

As you'd expect, this is failing on the second line, where I try to set propertyA.

What should I do instead? Should I create a vanilla subclass of NSObject that has the exact same attributes/properties as my NSManagedObject object? The proposed solution in the question I linked to talks about NSInMemoryStoreType, but that just seems overkill when all I really want is a convenient way to pass around an object. It's just that in this case, my object is an NSManagedObject, so I'm limited in what I can do with it.

1
When you say "this is failing", what exactly is happening?Anomie
It's failing with: 'NSInvalidArgumentException', reason: '-[Event setTimestamp:]: unrecognized selector sent to instance 0x1c0d50' because all the properties @dynamic instead of @syntehesize[d].djibouti33

1 Answers

0
votes

I wrote a category on NSManagedObject a while ago that creates an NSDictionary representation of the managed object. You can use the NSDictionary outside of the managed object context. Disclaimer: this code hasn't been thoroughly tested, and also note that it only handles the attributes of the managed object and does not handle relationships.

NSManagedObject+CLDAdditions.h
------------------------------

@interface NSManagedObject (CLDAdditions)
- (NSDictionary*)cld_dictionaryRepresentation;
@end

NSManagedObject+CLDAdditions.m
------------------------------

@implementation NSManagedObject (CLDAdditions)

- (NSDictionary*)cld_dictionaryRepresentation
{
    // Create empty dictionary
    NSMutableDictionary *objectDictionary = [NSMutableDictionary dictionary];
    // Set the entity
    [objectDictionary setObject:[[self entity] name] forKey:@"entity"];
    NSDictionary *attributeKeys = [[self entity] attributesByName];
    // Go through each of the attributes and add them to the dictionary
    for (NSString *attributeKey in attributeKeys) {
        id attributeValue = [self valueForKey:attributeKey];
        if (attributeValue) {
            // Supported objects
            if ([attributeValue isKindOfClass:[NSNumber class]] || [attributeValue isKindOfClass:[NSString class]] || [attributeValue isKindOfClass:[NSNull class]] || [attributeValue isKindOfClass:[NSDate class]] || [attributeValue isKindOfClass:[NSData class]] || [attributeValue isKindOfClass:[NSURL class]]) {
                [objectDictionary setObject:attributeValue forKey:attributeKey];
            // Unsupported objects
            } else if ([attributeValue isKindOfClass:[NSDictionary class]] || [attributeValue isKindOfClass:[NSArray class]]) {
                [NSException raise:NSGenericException format:@"Objects of type \"%@\" are not supported as Core Data attributes.", [attributeValue class]];
            // Transformable objects (conforming to NSCoding)
            // TODO: Add support for custom value transformers
            } else if (([[attributeKeys objectForKey:attributeKey] attributeType] == NSTransformableAttributeType) && [attributeValue conformsToProtocol:@protocol(NSCoding)]) {
                NSData *attributeData = [NSKeyedArchiver archivedDataWithRootObject:attributeValue];
                [objectDictionary setObject:attributeData forKey:attributeKey];
            // Otherwise raise an exception
            } else {
                [NSException raise:NSGenericException format:@"Unsupported object type \"%@\". Objects must conform to the NSCoding protocol.", [attributeValue class]];
            }
        }
    }
    return objectDictionary;
}

@end