Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding relationships in NSManagedObjectModel to programmatically created NSEntityDescription

When you write a static library which uses CoreData there's a big mess including a normal .xdatamodeld file into the project because you simply cannot just link its compiled version (.momd) into your binary, so it's better to create the whole NSManagedObjectModel in the code like this:

NSAttributeDescription *dateAttribute = NSAttributeDescription.new;

dateAttribute.name = @"timestamp";
dateAttribute.attributeType = NSDoubleAttributeType;
dateAttribute.optional = NO;
dateAttribute.indexed = YES;

NSAttributeDescription *payloadAttribute = NSAttributeDescription.new;

payloadAttribute.name = @"payload";
payloadAttribute.attributeType = NSBinaryDataAttributeType;
payloadAttribute.optional = NO;
payloadAttribute.indexed = NO;

NSEntityDescription *entry = NSEntityDescription.new;

entry.name = entry.managedObjectClassName = NSStringFromClass(MyCustomEntry.class);
entry.properties = @[dateAttribute, payloadAttribute];

NSManagedObjectModel *mom = NSManagedObjectModel.new;

mom.entities = @[entry];

And everything is just perfect....

But! Wait, if I have more than one entity in my NSManagedObjectModel and they are related (to-many, inversed, and so on), how in the world I gonna connect them in the code, like in example above, without that nice Xcode editor, where you make relationships with several mouse clicks?

Example

Imagine, we have a class MyCustomElement, which is almost the same as in MyCustomEntry from the code above. Now, here're their interfaces how they would appear if I used Xcode generation for entities:

@interface MyCustomEntry : NSManagedObject

@property (nonatomic, retain) NSNumber *timestamp;
@property (nonatomic, retain) NSData *payload;
@property (nonatomic, retain) MyCustomElement *element;

@end

@interface MyCustomElement : NSManagedObject

@property (nonatomic, retain) NSNumber * timestamp;
@property (nonatomic, retain) NSString * identifier;
@property (nonatomic, retain) NSSet *entries;

@end

@interface MyCustomElement (CoreDataGeneratedAccessors)

- (void)addEntriesObject:(MyCustomEntry *)value;
- (void)removeEntriesObject:(MyCustomEntry *)value;
- (void)addEntries:(NSSet *)values;
- (void)removeEntries:(NSSet *)values;

@end

What NSRelationshipDescription I need to create for them and how to init it?

like image 542
shoumikhin Avatar asked Dec 06 '12 12:12

shoumikhin


People also ask

How do you add relationships in Xcode?

Add Relationships To add a relationship, do the following: Select the graph editor style to view all your app's entities. Control-drag from one entity to another to create a pair of relationships.

What is transformable in Core Data?

Core Data allows us to store integers, booleans, strings, UUID, date, etc. but sometimes we want to store a specific data type like UIColor, UIImage, our own class, struct, or enum, and even arrays, but that is simply not an option in Attribute's Type.

What is NSManagedObjectContext in Swift?

An object space to manipulate and track changes to managed objects.

What is NSManagedObjectModel?

NSManagedObjectModel provides an API to retrieve a stored fetch request by name, and to perform variable substitution—see fetchRequestTemplate(forName:) and fetchRequestFromTemplate(withName:substitutionVariables:) . You typically define fetch request templates using the Data Model editor in Xcode.


1 Answers

Relationships are described by NSRelationshipDescription objects. The following code creates two entity descriptions for "MyCustomEntry", "MyCustomElement" with relationships

  • entries (MyCustomElement --> MyCustomEntry, to-many),
  • element (MyCustomEntry --> MyCustomElement, to-one), inverse of entries.

Both entities have only a string attribute "identifier" (to save some lines of code).

Objective-c:

NSEntityDescription *entry = [[NSEntityDescription alloc] init];
[entry setName:@"MyCustomEntry"];
[entry setManagedObjectClassName:@"MyCustomEntry"];

NSAttributeDescription *entryIdAttribute = [[NSAttributeDescription alloc] init];
entryIdAttribute.name = @"identifier";
entryIdAttribute.attributeType = NSStringAttributeType;

NSEntityDescription *element = [[NSEntityDescription alloc] init];
[element setName:@"MyCustomElement"];
[element setManagedObjectClassName:@"MyCustomElement"];

NSAttributeDescription *elementIdAttribute = [[NSAttributeDescription alloc] init];
elementIdAttribute.name = @"identifier";
elementIdAttribute.attributeType = NSStringAttributeType;

// To-many relationship from "Element" to "Entry":
NSRelationshipDescription *entriesRelation = [[NSRelationshipDescription alloc] init];

// To-one relationship from "Entry" to "Element":
NSRelationshipDescription *elementRelation = [[NSRelationshipDescription alloc] init];

[entriesRelation setName:@"entries"];
[entriesRelation setDestinationEntity:entry];
[entriesRelation setMinCount:0];
[entriesRelation setMaxCount:0]; // max = 0 for to-many relationship
[entriesRelation setDeleteRule:NSCascadeDeleteRule];
[entriesRelation setInverseRelationship:elementRelation];

[elementRelation setName:@"element"];
[elementRelation setDestinationEntity:element];
[elementRelation setMinCount:0];
[elementRelation setMaxCount:1]; // max = 1 for to-one relationship
[elementRelation setDeleteRule:NSNullifyDeleteRule];
[elementRelation setInverseRelationship:entriesRelation];

[entry setProperties:@[entryIdAttribute, elementRelation]];
[element setProperties:@[elementIdAttribute, entriesRelation]];

NSManagedObjectModel *mom = [[NSManagedObjectModel alloc] init];
[mom setEntities:@[entry, element]];

Swift (now updated for Swift 3/4):

let entry = NSEntityDescription ()
entry.name = "MyCustomEntry"
entry.managedObjectClassName = "MyCustomEntry"

let entryIdAttribute = NSAttributeDescription()
entryIdAttribute.name = "identifier";
entryIdAttribute.attributeType = .stringAttributeType;

let element = NSEntityDescription()
element.name = "MyCustomElement"
element.managedObjectClassName = "MyCustomElement"

let elementIdAttribute =  NSAttributeDescription()
elementIdAttribute.name = "identifier"
elementIdAttribute.attributeType = .stringAttributeType

// To-many relationship from "Element" to "Entry":
let entriesRelation = NSRelationshipDescription()

// To-one relationship from "Entry" to "Element":
let elementRelation = NSRelationshipDescription ()

entriesRelation.name = "entries"
entriesRelation.destinationEntity = entry
entriesRelation.minCount = 0
entriesRelation.maxCount = 0  // max = 0 for to-many relationship
entriesRelation.deleteRule = .cascadeDeleteRule
entriesRelation.inverseRelationship = elementRelation

elementRelation.name = "element"
elementRelation.destinationEntity = element
elementRelation.minCount = 0
elementRelation.maxCount = 1 // max = 1 for to-one relationship
elementRelation.deleteRule = .nullifyDeleteRule
elementRelation.inverseRelationship = entriesRelation

entry.properties = [entryIdAttribute, elementRelation]
element.properties = [elementIdAttribute, entriesRelation]

let mom = NSManagedObjectModel()
mom.entities = [entry, element]

I have tested this code and it seems to work, so I hope that it will be useful to you.

like image 158
Martin R Avatar answered Nov 24 '22 07:11

Martin R