Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core Data : Insertion into One to Many Relationship

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 ?

like image 972
Kyuubi Avatar asked Jun 10 '13 07:06

Kyuubi


People also ask

What are relationships in Core Data?

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.

Can we do multithreading with Core Data?

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.

What is codegen in Core Data?

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.


2 Answers

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
}
like image 90
geo Avatar answered Nov 07 '22 09:11

geo


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
like image 45
p.balmasov Avatar answered Nov 07 '22 09:11

p.balmasov