6

I am working with several NSManagedObject types with several relationships. How can I tell Core Data to automatically populate object IDs for me? I'm looking for something like an index key in SQL, so that no two instances of a given object are allowed to have the same ID.

Edit:

I'd like for all of my "Account" objects to have unique IDs on them. I was just adding one to the `countForFetchRequest, but I realized that when deleting the second to last object and then adding one, the last two objects now have the same IDs.

How can I ensure that a given value has a unique value for all instances of my "Account" NSManagedObject?

EDIT2:

I need to have a separate ID for sorting purposes.

Moshe
  • 57,511
  • 78
  • 272
  • 425

4 Answers4

10

All NSManagedObjects automatically have a unique NSManagedObjectID. There is no notion of a custom auto-incrementing attribute, but it's certainly easy to write one yourself.

Barry Wark
  • 107,306
  • 24
  • 181
  • 206
  • Can I use the NSManagedObjectID to grab that specific item from the Core Data store? – Moshe Jun 16 '11 at 18:42
  • Yes, see http://stackoverflow.com/questions/4720182/core-data-the-primary-key-id-of-a-row-in-the-database/4725959#4725959 – Jano Jun 16 '11 at 19:20
  • I actually ended up writing my own auto-increment using an aggregate. See my answer please, if you want to. +1 for you. – Moshe Jun 16 '11 at 23:42
  • 3
    'Easy' is not the word I would use. 'Painful' is more apt. It's understandably not Core Data's job. But still... – fatuhoku Apr 07 '14 at 11:42
4

The way I resolved this is with Core Data aggregates. I actually end up assigning the ID myself.

Essentially, I query Core Data for all of the entity IDs of my entity and then iterate through them. If I find an ID which is higher than the current temporary one, I make the temporary ID higher one higher than the aggregated one. When I'm done, I automatically have an ID which is higher than the highest one in the list. The only flaw I see with this is if there is a missing ID. (I believe that there is a simple fix for this as well.)

//
//  Create a new entity description
//

NSEntityDescription *entity = [NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:self.managedObjectContext];

//
//  Set the fetch request
//

NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
[fetchRequest setEntity:entity];

//
//  We need to figure out how many 
//  existing groups there are so that 
//  we can set the proper ID.
//
//  To do so, we use an aggregated request.
//

[fetchRequest setResultType:NSDictionaryResultType];
[fetchRequest setPropertiesToFetch:[NSArray arrayWithObject:@"entityID"]];

NSError *error = nil;

NSArray *existingIDs = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];


if (error != nil) {

    //
    //  TODO: Handle error.
    //

    NSLog(@"Error: %@", [error localizedDescription]);
}

NSInteger newID = 0;

for (NSDictionary *dict in existingIDs) {
    NSInteger IDToCompare = [[dict valueForKey:@"entityID"] integerValue];

    if (IDToCompare >= newID) {
        newID = IDToCompare + 1;
    }
} 

//
//  Create the actual entity
//

MyEntity *newEntity = [[MyEntity alloc] initWithEntity:entity insertIntoManagedObjectContext:self.managedObjectContext];

//
//  Set the ID of the new entity
//

[newEntity setEntityID:[NSNumber numberWithInteger:newID]];

//
//   ... More Code ...
//
Moshe
  • 57,511
  • 78
  • 272
  • 425
  • You needn't to fetch all rows - just last using descending sort by id predicate – HotJard Oct 06 '13 at 15:20
  • 2
    if you uses magical record entity.entityID = @([[Entity findFirstOrderedByAttribute:@"entityID" ascending:NO] entityID].intValue+1); – Eldhose Dec 07 '13 at 04:13
3

Accroding to your EDIT2 and Edit3, following answer will help you.. Assume your id field as NSNumber having unsignedInt as ID.

1) Fetch all records for corresponding entity.

NSError *error = nil;
NSArray *array = [self fetchAllFileEntity:&error];

2) Find maximum number belonging to that result.

NSNumber *maxValue = nil;
if (array)
    maxValue = [array valueForKeyPath:@"@max.uniqueId.unsignedIntegerValue"];
else
    maxValue = [NSNumber numberWithUnsignedInteger:0];

3) Assign maxValue+1 to your new entity

entity.uniqueId = [NSNumber numberWithUnsignedInteger:maxValue.unsignedIntegerValue+1];
Mani
  • 17,549
  • 13
  • 79
  • 100
  • 2
    what happen when i delete some of the rows(rows with the maximum value) – souvickcse May 13 '14 at 10:01
  • 1
    explain with ex: Assume if you've 5 row means, @max count is 5. Now you'd delete 5th row means, max count is 4. So when you're try to add new, automatically assign 5 to new one, that is, all row if exist at table should have unique value(local db). – Mani May 13 '14 at 10:13
  • i am facing the same issue can any one can provide proper solution. – Ayaz Dec 27 '15 at 07:44
1

I have come up with this solution for the said problem, hope it's gonna be helpful for some one.

AppDelegate *appdelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate];

NSManagedObjectContext *context = [appdelegate managedObjectContext];

NSError *error = nil;

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

NSEntityDescription *chatHist = [NSEntityDescription
                                     entityForName:@"ChatHistory" inManagedObjectContext:context];
[fetchRequest setEntity:chatHist];

int chatIdNumber = 0;

NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if ([fetchedObjects count] > 0) {
    ChatHistory *chatHistObj = [fetchedObjects objectAtIndex:[fetchedObjects count]-1];
    chatIdNumber = [chatHistObj.chatId intValue];
}

chatIdNumber = chatIdNumber+1;
ChatHistory *chat_History  = [NSEntityDescription          insertNewObjectForEntityForName:@"ChatHistory" inManagedObjectContext:context];

chat_History.chatId = [NSString stringWithFormat:@"%d",chatIdNumber];