Search code examples
iosobjective-ccore-datansfetchedresultscontrollernsmanagedobject

Add new attribute to existing Core Data Entity Relationship


I'm not sure if my understanding of Core Data relationships is flawed as I can't seem to achieve what I want to do.

I have a 2 entities created to manage Chat on the app and a one-to-Many relationship between the users and the messages. So a user can have many messages but the messages have just you user (creator).

enter image description here

I am trying to update the ChatUser entity relationship when a new message is added whereby a connection between the ChatUser ID and the ChatMessage is established. I can do this but the issue arises when I go to add a new message to an existing userId. All that is currently being achieved though is adding an extra userId into ChatUser instead of adding only the relationship to the existing UserId.

NSManagedObjectContext *context = [self managedObjectContext];
NSError *error = nil;

// 4 . Get Timestamp for Rippll
float timestamp = @([[NSDate date] timeIntervalSince1970]).floatValue;
 NSString * jayID = @"eu-west-1:be6457ce-bac1-412d-9307-e375e52e22ff";

  NSString *message = @"Science string!";

// Create a new managed object
ChatUser *chatUserManagedObject = [NSEntityDescription insertNewObjectForEntityForName:@"ChatUser" inManagedObjectContext:context];
Chat *chatManagedObject = [NSEntityDescription insertNewObjectForEntityForName:@"ChatMessage" inManagedObjectContext:context];
Timeline *timelineManagedObject = [NSEntityDescription insertNewObjectForEntityForName:@"Timeline" inManagedObjectContext:context];

// 3 . Save Timeline
[timelineManagedObject setEvent:chatEvent];
[timelineManagedObject setTimestamp:[NSNumber numberWithFloat:timestamp]];
[timelineManagedObject setMeta:@""];
[timelineManagedObject setViewed:@NO];
[timelineManagedObject setEventID:jayID];

//Save UserMessage
[chatManagedObject setChatId:jayID];
[chatManagedObject setTimestamp:[NSNumber numberWithFloat:timestamp]];
[chatManagedObject setMessage:message];
[chatManagedObject setMedia:@""];

//Check if value exists
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"ChatUser"];
[request setPredicate:[NSPredicate predicateWithFormat:@"userId = %@", jayID]];
[request setFetchLimit:1];

NSArray *entities = [[context executeFetchRequest:request error:&error] mutableCopy];

if (entities.count == 0) {

    NSLog(@"GOOD TO ADD");
        // no matching object

    [chatUserManagedObject setUserId:jayID];

    //Create Relationship
    [chatUserManagedObject addChatObject:chatManagedObject];

} else {

    NSLog(@"IT EXISTS!");

    [chatManagedObject setChat:chatUserManagedObject];

}

// Save the object to persistent store
if (![context save:&error]) {
    NSLog(@"Can't Save! %@ %@", error, [error localizedDescription]);
}

Solution

  • I thin k, @PangHoMing was on the right track, but used Magical Records. Let's do it solely with CD:

    First rename the relationships. Probably in ChatMessage there should be a to-1 relationship named user (or chatUser) and in ChatUser there should be a to-N relationship messages (or chatMessages). They should be inverse relationship.

    Next you should ask for the existence of a user before creating it. (Otherwise you create phantom users.) You got the code for it:

    // Create message as you did
    ChatMessage *message = …;
    …
    
    // The user will go here
    ChatUser *user; // Do not use types in names unless conversion is subject of your code
    
    // Look for an existing one
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"ChatUser"];
    [request setPredicate:[NSPredicate predicateWithFormat:@"userId = %@", jayID]];
    [request setFetchLimit:1];
    
    NSArray *entities = [[context executeFetchRequest:request error:&error] mutableCopy];
    
    if (entities.count == 0) 
    {
      // Only if there is none, create one  
      user = [NSEntityDescription insertNewObjectForEntityForName:@"ChatUser" inManagedObjectContext:context];
      // Set-up user's properties
      …
    } 
    else 
    {
      // Use the existing one
      user = entities[0];
    }
    
    [message setValue:user forKey:@"user"]; // message.user = user;
    

    As mentioned by others, the inverse relationship is maintained by CD. However, you can use the inverse relationship to add the message, if this is more readable for you:

    [[user mutableSetValueForKey:@"messages"] addObject:message]; // [user addMessagesObject:message]
    

    This will maintain the "original" relationship, too.