13
votes

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 ?

2

2 Answers

21
votes

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: enter image description here

In Restaurant: enter image description here

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
}
1
votes
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