Search code examples
iphoneioscore-datarestkit

primaryKeyAttribute not working Restkit/Core Data


I just installed the framework restkit 0.9.3 and followed the Discussion Board example. Well, everything just worked great, however when I tried to use Core Data my User NSManagedObject class is duplicating even after declaring his primaryKeyAttribute (userID). For example, when I send a login request to my web-server, I return {"user":{"id":1, "username":"teste", ...}} .. but it seems to create a new row every time it invoques objectLoader:didLoadObjects.

User table:

enter image description here

Example code:

~ AppDelegate.m didFinishLaunching

RKManagedObjectMapping* userMapping = [RKManagedObjectMapping mappingForClass:[User class]];    
userMapping.primaryKeyAttribute = @"userID";
userMapping.setDefaultValueForMissingAttributes = YES; // clear out any missing attributes (token on logout)
[userMapping mapKeyPathsToAttributes:
     @"id", @"userID",
     @"email", @"email",
     @"username", @"username",
     @"password", @"password",
     nil];

[objectManager.mappingProvider registerMapping:userMapping withRootKeyPath:@"user"];

~ User.m loginWithDelegate

- (void)loginWithDelegate:(NSObject<UserAuthenticationDelegate>*)delegate {
    _delegate = delegate;   
    [[RKObjectManager sharedManager] postObject:self delegate:self block:^(RKObjectLoader* loader) {
        loader.resourcePath = @"/login";
        loader.serializationMapping = [RKObjectMapping serializationMappingWithBlock:^(RKObjectMapping* mapping) {
            [mapping mapAttributes:@"username", @"password", nil];            
        }];
    }];
}

~ User.m didLoadObjects (RKObjectLoaderDelegate)

- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray *)objects {

    if ([objectLoader wasSentToResourcePath:@"/login"]) {
        [self loginWasSuccessful];
    }

    NSLog(@"number of user rows: %i", [User findAll].count);
}

What am I doing wrong?


Solution

  • Are you correctly implementing RKManagedObjectCache? For debugging I had it simply return nil and forgot about that. A little while later I found I had duplicates also.

    The cache works by fetching local objects and comparing with server returned objects. Any local objects that are not in the server response will be deleted. In earlier versions it used a fetch request but in newer versions you must manually perform the request and return actual objects.

    If you return nil, it thinks this object is not in your cache and will add a duplicate. Try implementing this method:

    + (NSManagedObject *)findInstanceOfEntity:(NSEntityDescription *)entity
                  withPrimaryKeyAttribute:(NSString *)primaryKeyAttribute
                                    value:(id)primaryKeyValue
                   inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext
    

    For example:

    + (NSManagedObject *)findInstanceOfEntity:(NSEntityDescription *)entity
                  withPrimaryKeyAttribute:(NSString *)primaryKeyAttribute
                                    value:(id)primaryKeyValue
                   inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext {
    
        NSFetchRequest* request = [[NSFetchRequest alloc] init];
        [request setEntity: entity];
        [request setFetchLimit: 1];
        [request setPredicate:[NSPredicate predicateWithFormat:@"%K = %@", primaryKeyAttribute, primaryKeyValue]];
    
        NSArray *results = [NSManagedObject executeFetchRequest:request inContext: managedObjectContext];
        if ([results count] == 0)
        {
         return nil;
        }
        return [results objectAtIndex:0];
    }