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