-
-
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).