Skip to content

Instantly share code, notes, and snippets.

@weejayuk
Forked from vl4dimir/ExtendedManagedObject.h
Created October 5, 2010 07:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save weejayuk/611157 to your computer and use it in GitHub Desktop.
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
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

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
Author

weejayuk commented Jun 27, 2011 via email

@wryphonedev
Copy link

Thanks. Yeah compiling with LLVM works fine.

@weejayuk
Copy link
Author

weejayuk commented Jun 27, 2011 via email

Copy link

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

Extremely helpful. Thanks!

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