-
-
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); | |
| } |
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){
Thanks. Yeah compiling with LLVM works fine.
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) {
Extremely helpful. Thanks!
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).