Search code examples
ioscore-datarestkit

RestKit putObject - should it handle persistence back to the Core Data store for me?


I'm working with RestKit 0.27.0 and have managed to setup my Core Data stack almost exactly like in the TwitterCoreData example in RestKits GitHub.

I've also mapped GET / POST requests which are persisting the Objects in Core Data correctly, though I am having trouble with my PUT request.

It appears that my PUT request is updating the object on the server correctly, and I can see it updated on the response. I then go to refresh the SQLite DB Viewer expecting the Entity to have been updated, but it hasn't.

Take addressLine1 for example, it was set to: "4 Some Address". I then update it to "4 TEST THIS CHANGE" on the NSManagedObject and pass it through as the Object on the PUT.

I can see the following in the logs:

2017-03-02 15:05:18.006 Albert[41779:1232025] T restkit.object_mapping:RKMappingOperation.m:748 Skipped mapping of attribute value from keyPath 'addressLine1 to keyPath 'addressLine1' -- value is unchanged (4 TEST THIS CHANGE)

It's almost as if because I've updated the addressLine1 value on the NSManagedObject, that RestKit doesn't pick it up as a change?

When I log back into my app it performs a GET request and when it gets the Object, it realises that there is a difference between the transient Object and the Entity stored in CoreData and so updates it successfully.

I can't find any putObject: documentation that can help me understand where I am going wrong, so based on the latest version of RestKit (0.27.0 as of the time of writing) - It would be good to be provided with or directed to an example starting from referencing an existing NSManagedObject, updating a property (attribute) on it, and then sending it as an argument in the PUT request. I imagine that RestKit "should" be handling the persistence to Core Data for me like it does on the GET and POST requests.


Solution

  • It looks like I misunderstood how RestKit works with CoreData.

    Originally I was first making the required update to the Object and then doing the PUT request like this:

    [[RKObjectManager sharedManager] putObject:myObjectWithChanges 
                                     path:path 
                                     parameters:nil 
        success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)
    

    I assumed that by passing it the NSManagedObject with changes, it would work correctly. It did update the server, but would screw up when it came to updating the Entity in CoreData - Seems RestKit assumed it was already up-to-date because I updated the local NSManagedObject.

    So instead I decided to try and use nil for the object and pass the changes as a dictionary in parameters (It isn't a requirement to provide an Object in RestKit if you are providing a path):

    NSDictionary *dict = @{ 
                            @"addressLine1":@"4 TEST THIS CHANGE 11",
                            @"addressLine2":@"Address line 2",
                            @"postCode":@"WN7 5GB"
                          };
    
    [[RKObjectManager sharedManager] putObject:nil           
                                     path:path 
                                     parameters:dict
        success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)
    

    Using this for the PUT allowed me to update both the object on the server and the local Entity in Core Data.

    I can only assume the "Object" in putObject: is only to tell RestKit which object it is, so that RestKit can choose the appropriate request / response descriptors for routing. It is the parameters: argument that must take any changes made for said object.

    NOTE: It is also fine to put your object into the call like so:

        NSDictionary *dictWithChanges = @{ 
                                            @"addressLine1":@"Change here"
                                         };
    
        [[RKObjectManager sharedManager] putObject:myObjectWithoutChanges           
                                         path:path 
                                         parameters:dictWithChanges
            success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)
    

    It appears that RestKit will merge changes on the parameters with the Object and reconstruct the request.