I have just started learning how to implement Core Data Model on the iOS. After some basic tutorials on how to store and retrieve data with one to one relationship among entities, I am now trying to implement a one to many relationship. My data model consists of two entities with their respective classes defined as follows:
Restaurant:
@interface Restaurant :NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSMutableArray *user_reviews; /* One to Many Relation to Review Entity*/
@end
Review:
@interface Review : NSObject
@property (nonatomic, strong) NSString *rating;
@property (nonatomic, strong) NSString *review_text;
@property (nonatomic, strong) User *user;
@end
I found similar questions that used NSMutableSet to insert, but I am unable to implement the same for an NSMutable Array.
Currently my insertion code looks like :
NSManagedObjectContext *context = [self managedObjectContext];
Restaurant *rest = [NSEntityDescription insertNewObjectForEntityForName:@"Restaurant" inManagedObjectContext:context];
rest.name = restaurant.name;
I retrieve the data for Restaurant and its Reviews via JSON and store them in temporary classes before saving them to the CORE Database. How do I insert such data which has One to Many Relationship ?
EDIT : Currently I receive the data in the form of a class which defines the user_review property as
NSMutableArray *user_reviews;
I am trying to implement this same class to insert into core data model. But the core data model uses NSSet instead of NSMutableArray. One brute approach is to duplicate all the classes with the same properties except instead of using NSMutableArray, i change it to NSSet. But this creates a huge amount of redundant code. Shouldn't there be a more efficient way to do this ?
Inverse relationships enable Core Data to propagate change in both directions when an instance of either the source or destination type changes. Every relationship must have an inverse. When creating relationships in the Graph editor, you add inverse relationships between entities in a single step.
Core Data is designed to work in a multithreaded environment. However, not every object under the Core Data framework is thread safe. To use Core Data in a multithreaded environment, ensure that: Managed object contexts are bound to the thread (queue) that they are associated with upon initialization.
The Codegen value is short for “code generation” – when you pressed Cmd+B to build your project, Xcode converted the Commit Core Data entity into a Commit Swift class. You can't see it in the project – it's dynamically generated when the Swift code is being built – but it's there for you to use, as you just saw.
if your Review shall be a relationship in your coreDataModel, please use instead of NSMutableArray
the NSSet
and connect it with the Restaurant Entity.
In Review:
In Restaurant:
If you let xcode generate your class, it will look like this:
Restaurant:
@interface Restaurant : NSManagedObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSSet *user_reviews; /* One to Many Relation to Review Entity*/
@interface Restaurant(CoreDataGeneratedAccessors)
- (void)addUser_reviewsObject:(Review *)value;
- (void)removeUser_reviewsObject:(Review *)value;
- (void)addUser_reviews:(NSSet *)value;
- (void)removeUser_reviews:(NSSet *)value;
@end
Review:
@interface Review : NSManagedObject
@property (nonatomic, strong) NSString *rating;
@property (nonatomic, strong) NSString *review_text;
@property (nonatomic, strong) User *user;
@property (nonatomic, strong) Restaurant *restaurant;
@end
Your call will be:
NSManagedObjectContext *context = [self managedObjectContext];
Restaurant *rest = [NSEntityDescription insertNewObjectForEntityForName:@"Restaurant" inManagedObjectContext:context];
rest.name = restaurant.name;
Review *rev = [NSEntityDescription insertNewObjectForEntityForName:@"Review" inManagedObjectContext:context];
rev.rating = @"1";
rev.review_text = @"nomnomnom";
[rest addUser_reviewsObject:rev];
// or rev.restaurant = restaurant; one of both is enought as far as I remember
// save your context
edit
If it has to be a NSMutableArray
, it cannot be a ralation.
Those are always NSSets
(if x to n) or the destination classes. Using NSMutableArray
takes the advantages of sets and the automatic handling.
But if you realy want to store in NSMutableArray
, I recommand to expand your Review
class at least by a reviewID
attribute (unique) and store the NSMutableArray
as Transformable
.
//Review:
@interface Review : NSManagedObject
@property (nonatomic, strong) NSString *rating;
@property (nonatomic, strong) NSString *review_text;
@property (nonatomic, strong) User *user;
@property (nonatomic, strong) NSNumber *reviewID;
@end
//Restaurant.h:
@interface Restaurant : NSManagedObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSSet *user_reviews; // Set of NSNumbers
- (void)addUser_reviewsObject:(Review *)value;
- (void)addUser_reviewsID:(NSNumber *)value;
- (void)removeUser_reviewsObject:(Review *)value;
- (void)addUser_reviews:(NSMutableArray *)value;
- (void)removeUser_reviews:(NSMutableArray *)value;
@end
//Restaurant.m:
- (void)addUser_reviewsObject:(Review *)value
{
[self addUser_reviewsID:value.reviewID];
}
- (void)addUser_reviewsID:(NSNumber *)value
{
if(![self.user_reviews containsObject:value];
[self.user_reviews addObject:value];
}
- (void)removeUser_reviewsObject:(NSNumber *)value
{
// follow upper logic and implement yourself
}
- (void)addUser_reviews:(NSMutableArray *)value
{
// follow upper logic and implement yourself
}
- (void)removeUser_reviews:(NSMutableArray *)value
{
// follow upper logic and implement yourself
}
Review *review = (Review *)[NSEntityDescription insertNewObjectForEntityForName:@"Review" inManagedObjectContext:[self managedObjectContext]];
review.review_text = @"test text";
//set other properties if needed
rest.user_reviews = [NSSet setWithObjects:review, nil];//just make NSSet for you reviews
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With