Skip to content

Instantly share code, notes, and snippets.

@weejayuk
Forked from vl4dimir/ExtendedManagedObject.h
Created October 5, 2010 07:16
Show Gist options
  • Select an option

  • Save weejayuk/611157 to your computer and use it in GitHub Desktop.

Select an option

Save weejayuk/611157 to your computer and use it in GitHub Desktop.
typedef BOOL (^BlockingRelationship)(id,NSString*);
@interface NSManagedObject (ExtendedManagedObject)
- (NSDictionary*) toDictionaryBlockingRelationships:(BOOL (^)(id obj,NSString *relationship))blockRelationship;
+ (NSManagedObject*) createManagedObjectFromDictionary:(NSDictionary*)dict
inContext:(NSManagedObjectContext*)context;
@end
#import "ExtendedManagedObject.h"
@implementation NSManagedObject (ExtendedManagedObject)
#pragma mark -
#pragma mark Dictionary conversion methods
- (NSDictionary*) toDictionaryBlockingRelationships:(BlockingRelationship) blockRelationship;
{
NSArray* attributes = [[[self entity] attributesByName] allKeys];
NSArray* relationships = [[[self entity] relationshipsByName] allKeys];
NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithCapacity:
[attributes count] + [relationships count] + 1];
[dict setObject:[[self class] description] forKey:@"class"];
for (NSString* attr in attributes) {
NSObject* value = [self valueForKey:attr];
if (value != nil) {
[dict setObject:value forKey:attr];
}
}
NSLog(@"Looking through relationships of %@",[self class]);
for (NSString* relationship in relationships) {
NSLog(@"Looking for relationship of %@",relationship);
if(!blockRelationship(self,relationship)){
NSObject* value = [self valueForKey:relationship];
if ([value isKindOfClass:[NSSet class]]) {
// To-many relationship
// The core data set holds a collection of managed objects
NSSet* relatedObjects = (NSSet*) value;
// Our set holds a collection of dictionaries
NSMutableSet* dictSet = [NSMutableSet setWithCapacity:[relatedObjects count]];
for (NSManagedObject *relatedObject in relatedObjects) {
NSDictionary *returnedDict=[relatedObject toDictionaryBlockingRelationships:blockRelationship];
if(returnedDict)
[dictSet addObject:returnedDict];
}
if(dictSet)
[dict setObject:dictSet forKey:relationship];
}
else if ([value isKindOfClass:[NSManagedObject class]]) {
// To-one relationship
NSDictionary *returnedDict=nil;
NSManagedObject* relatedObject = (NSManagedObject*) value;
returnedDict=[relatedObject toDictionaryBlockingRelationships:blockRelationship];
if (returnedDict)
[dict setObject:returnedDict forKey:relationship];
}
}
}
return dict;
// traversed=YES;
}
+ (NSManagedObject*) createManagedObjectFromDictionary:(NSDictionary*)dict
inContext:(NSManagedObjectContext*)context
{
NSString* class = [dict objectForKey:@"class"];
NSManagedObject* newObject =
(NSManagedObject*)[NSEntityDescription insertNewObjectForEntityForName:class
inManagedObjectContext:context];
for (NSString* key in [dict allKeys]) {
if ([key isEqualToString:@"class"]) {
continue;
}
NSObject* value = [dict objectForKey:key];
if ([value isKindOfClass:[NSDictionary class]]) {
// This is a to-one relationship
NSManagedObject* relatedObject =
[NSManagedObject createManagedObjectFromDictionary:(NSDictionary*)value
inContext:context];
[newObject setValue:relatedObject forKey:key];
}
else if ([value isKindOfClass:[NSSet class]]) {
// This is a to-many relationship
NSSet* relatedObjectDictionaries = (NSSet*) value;
// Get a proxy set that represents the relationship, and add related objects to it.
// (Note: this is provided by Core Data)
NSMutableSet* relatedObjects = [newObject mutableSetValueForKey:key];
for (NSDictionary* relatedObjectDict in relatedObjectDictionaries) {
NSManagedObject* relatedObject =
[NSManagedObject createManagedObjectFromDictionary:relatedObjectDict
inContext:context];
[relatedObjects addObject:relatedObject];
}
}
else if (value != nil) {
// This is an attribute
[newObject setValue:value forKey:key];
}
}
return newObject;
}
@end
//Set this up as you like. Based on you object model. In the model you have Tables and relationships.
//Pick the ones where you want the recursion to stop, and look for them in the block and return YES
// when you find them.
BlockingRelationship blockRelationship=^BOOL(id obj,NSString* relationship)){
NSLog(@"Testing blockRelationship in %@",[obj class]);
BOOL preventRelationship=NO;
if ([obj isKindOfClass:NSClassFromString(@"TableA")]){
if([relationship isEqualToString:@"activeParent"]|[relationship isEqualToString:@"relationshipA"])
preventRelationship=YES;
}
else if ([obj isKindOfClass:NSClassFromString(@"TableB")]){ //block all relationships from here.
preventRelationship=YES;
}
else if ([obj isKindOfClass:NSClassFromString(@"TableC")]){ //block all relationships from here.
preventRelationship=YES;
}
else if ([obj isKindOfClass:NSClassFromString(@"TableD")]){
if([relationship isEqualToString:@"aRelationship"]) //many to many - block back
preventRelationship=YES;
}
else if ([obj isKindOfClass:NSClassFromString(@"TableE")]){
if([relationship isEqualToString:@"bRelationship"]|([relationship isEqualToString:@"cRelationship"]))
preventRelationship=YES;
}
else if ([obj isKindOfClass:NSClassFromString(@"TableF")]){
preventRelationship=YES;
}
return preventRelationship;
};
//this is test code..call to make the dictionary from the managed object.
NSDictionary *returnedDict=[YourNSObject toDictionaryBlockingRelationships:blockRelationship];
NSLog(@"%@",returnedDict);
//create another object model from your NSDictionary.
YourNSObject *newObjects =(YourNSObject*)[YourNSObject createManagedObjectFromDictionary:returnedDict
inContext:YourNSObject.managedObjectContext];
NSManagedObjectContext *context = templateVersion.managedObjectContext;
NSError *error = nil;
if (![context save:&error]) {
NSLog(@"think we might get here%@",error);
}
@weejayuk
Copy link
Copy Markdown
Author

weejayuk commented Oct 5, 2010

This is built on Vladimir Zardina's excellent work. http://vladimir.zardina.org/ I suggest you read his blog "Serializing (Archiving/Unarchiving) an NSManagedObject Graph" first to get the point of what I am doing.
Basically it means you can traverse as much of an object model as you like and put it into a dictionary.
If you have a large object model and want to put different bits into different dictionaries, you can by only creating different blocks.

I have changed it so that the subclass is replaced with a category.
Do a #import "ExtendedManagedObject.h" wherever you need to add the functionality.
I have got rid of the need to have a variable (tranversed), since these don't work with categories and I don't need it.
I have added a block which allows you to specify which bits of the object model get recursed. Blocks will only work with iOS4 and Mac OS 10.6 (not tested on 10.6).

@wryphonedev
Copy link
Copy Markdown

This is very useful - however, it won't compile. I'm new to blocks in Obj-C and am just learning the syntax and construct, I'm a bit confused. I think it has something to do with using a pointer to a primitive BOOL, but I really don't know.

After placing the block in my method, I get this compiler error:
Incompatible block pointer types initializing 'signed char (^)(struct object_object , struct NSString)', expected 'BOOL (^)(struct objc_object *)'

BOOL (^ blockRelationship)(id, id)=^(id obj, NSString* relationship){

@weejayuk
Copy link
Copy Markdown
Author

weejayuk commented Jun 27, 2011 via email

@wryphonedev
Copy link
Copy Markdown

Thanks. Yeah compiling with LLVM works fine.

@weejayuk
Copy link
Copy Markdown
Author

weejayuk commented Jun 27, 2011 via email

Copy link
Copy Markdown

ghost commented Apr 23, 2012

Just updating about the fix that can be found here: http://stackoverflow.com/questions/6503823/compilation-issue-when-using-blocks-with-llvm-gcc-4-2

Change

BlockingRelationship blockRelationship=^BOOL(id obj,NSString* relationship)){

to

BOOL (^blockRelationship)(id,NSString_)=^(id obj,NSString_ relationship) {

@ExoticObjects
Copy link
Copy Markdown

Extremely helpful. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment