RestKit serialization creating duplicate entries - ios

When saving to my database with Restkit, I get duplicate entries.
I'm not sure how to prevent this. The intended behavior is that if the object already exists, then it should update that existing object with the columns that happen to be different.
I set a key identifier here:
[mapping setIdentificationAttributes:#[MYObjectAttributes.userID]];
but I suppose there is something else I am supposed to do. I've seen other questions more related to core-data that manually do a fetch request looking for an existing entry, before writing it, this seems expensive and restkit is supposed to have a solution for this already.

RestKit is for mapping a RESTful service to core data. If you are not using the RKObjectManager for updating (that is, you want to put something on your REST service) and only want to do a local change you should get the managed object and work with it outside the context of RestKit.
If you need to check whether a managed object exists locally or not, you should do it with a Managed Object Context rather than try to use RestKit for it.

Along with attributes, you can also detect, whether managed object is new or not. RestKit has created a category over NSManagedObject, where it has provided 1 function:
/**
* Returns YES when an object has not been saved to the managed object context yet
*/
#property (nonatomic, readonly) BOOL isNew;
https://github.com/RestKit/RestKit/blob/fc101de9133d96bc0e2221153de7f699f8c1f06d/Code/CoreData/NSManagedObject%2BRKAdditions.m

Related

Can I specify a objectId when creating new PFObjects?

Swift / iOS
Can anyone tell me if it is possible to specify the PFObject objectId value when creating new objects?
I've obviously attempted but the save fails. (which might just be the answer)
The reason I am asking is I wondered if anyone had found a "trick" that would allow me to specify.
I am using PFObject.saveInBackground { method to persist the new object.
No you can not. Parse sets the objectId on the server during the save operation.
The reason your operation is failing is because Parse is looking for an object on the server with the id that you are specifying and is then trying to update that object but it cannot find the object.

Why does storing a reference to an NSManagedObject prevent it from updating?

This question is poorly phased but this can be better explained in code.
We have a Core Data Stack with private and main contexts as defined by Marcus Zarra here: http://martiancraft.com/blog/2015/03/core-data-stack/
We call a separate class to do a fetch request (main context) and return an array of NSManagedObjects:
NSArray *ourManagedObjects = [[Client sharedClient].coreDataManager fetchArrayForClass:[OurObject class] sortKey:#"name" ascending:YES];
We then do some processing and store a reference:
self.ourObjects = processedManagedObjects
Our view contains a UITableView and this data is used to populate it and that works just fine.
We change the data on our CMS, pull to refresh on the UITableView to trigger a sync (private context) and then call this same function to retrieve the updated data. However, the fetch request returns the exact same data as before even though when I check the sqlite db directly it contains the new data. To get the new values to display I have to reload the app.
I have discovered that if I don't assign the processedManagedObjects to self, the fetch request does indeed return the correct data, so it looks like holding a reference to the NSManagedObject stops it from getting new data from the main context. However I have no idea why that would be.
To clarify, we're pretty sure there's nothing wrong with our Core Data Stack, even when these managed objects are not being updated, other are being updated just fine, it's only this one where we store a local reference.
It sounds like what's going on is:
Managed objects don't automatically update themselves to reflect the latest data in the persistent store when changes are made via a different managed object context.
As a result, if you keep a reference to the objects, they keep whatever data they already had.
On the other hand if you don't keep a reference but instead re-fetch them, you get the new data because there was no managed object hanging around with its old data.
You have a few options:
You could keep the reference and have your context refresh the managed objects, using either the refresh(_, mergeChanges:) method or refreshAllObjects().
If it makes sense for your app, use an NSFetchedResultsController and use its delegate methods to be notified of changes.
Don't keep the reference.
The first is probably best-- refreshAllObjects() is probably what you want. Other options might be better based on other details of your app.
Try setting the shouldRefreshRefetchedObjects property of the fetch request to true. According to the documentation:
By default when you fetch objects, they maintain their current property values, even if the values in the persistent store have changed. Invoking this method with the parameter true means that when the fetch is executed, the property values of fetched objects are updated with the current values in the persistent store.

Cannot delete objects in other context

I am facing this problem consistently for over 3 months. I have searched a lot and read related docs and visited many forums but couldn't find working solution. I am getting typical NSManagedObject error while deleting objects. An NSManagedObjectContext cannot delete objects in other contexts.
I tried to go around and tried to delete object using its NSManagedObject ID but to no avail.
NSManagedObjectID *findingsSurveyDataItemApiId = [findingsSurveyDataItemApi objectID];
[self.managedObjectContext deleteObject:[self.managedObjectContext objectWithID:findingsSurveyDataItemApiId]];
Can anyone tell why is above solution still not working?
PS: I have two managed object context in the app.
I guess it might be a misleading error message from Core Data. If the object you want to delete has not yet been saved to the persistent store, objectWithID will not return a valid object, according to the docs:
The data in the persistent store represented by objectID is assumed to exist—if it does not, the returned object throws an exception when you access any property (that is, when the fault is fired).
Use existingObjectWithID:error: instead and check if it returns a non-nil object before trying to delete it.

RestKit 0.20.3 + Core Data - Skip mapping operation

I'm working on an app and I need to sync objects with my API. I use RestKit 0.20.3.
The sync process begins by pulling objects from the server, and then pushes the objects that have been modified inside the app by the user.
Therefore, during the pull step, I need to ignore the objects that have been modified locally, so they won't be overriden with the server version.
I use CoreData for my objects, and I set a boolean property "modified" to YES for those that are locally modified.
So after a GET during the sync, I need to skip the mapping step for the objects having this "modified" property, but I can't find exactly how I'm supposed to do that.
The only way I've found so far is by adding a condition directly inside the RKMappingOperation, but it's dirty.
Is there a better way to do that in RestKit (and by not modifying the RestKit code)?
Well, I kept on investigating, and I just found the solution.
It's actually possible to register a custom class as RKResponseMapperOperation data source :
[RKManagedObjectResponseMapperOperation registerMappingOperationDataSourceClass:[MyCustomDataSource class]];
MyCustomDataSource should implement the RKMappingOperationDataSource protocol.
Mine inherits from RKManagedObjectMappingOperationDataSource since I'm using managed objects through Core Data.
#interface SUManagedObjectMappingOperationDataSource : RKManagedObjectMappingOperationDataSource
#end
In the implementation, I just overridden the following method to add my skipping logic to the existing one :
- (BOOL)mappingOperationShouldSkipPropertyMapping:(RKMappingOperation *)mappingOperation

Can I "manually" insert items into an AFIncrementalStore's cache?

I have set up AFIncrementalStore to grab objects from a JSON service over the network and set its persistentStore to be an SQLite database. This all works fine.
Now what I want to do is add objects to that SQLite database out-of-band (from something other than the web service the AFIncrementalStore is pointing to), and have those additions reflected in the fetched results controllers created from the original managed object context.
I've created a managed object context with the original MOC as its parent and I can add objects to that and they're seen by the fetched results controller. But they're not saved to the AFIncrementalStore's SQLite db. Interestingly, AFIncrementalStore is seeing these objects as it was trying to save them back to the JSON service and complaining the correct endpoint didn't exist (I fixed this by overriding requestForInsertedObject:insertedObject to return nil.)
Anyone know how I achieve this?
If you don't want to POST objects to the server you have to override methods
requestForInsertedObject:insertedObject
requestForUpdatedObject:updatedObject
requestForDeletedObject:deletedObject
Then when you call context's save: method your objects must be saved in the database. I'm using similar logic when I'm doing CRUD operations offline and it is working fine.
It sounds like you have already found most of the answer. AFIncrementalStore checks for a nil response from requestForInsertedObject: in your AFRESTClient subclass. If that method returns nil, AFIS creates a permanent ID for the object, stores the object in the backing store and doesn't try to send it to the server again. This is all in the first section of executeSaveChangesRequest:withContext:error:.
Are you always calling save: on the parent ManagedObjectContext? If not, that would be the other reason it's not storing the object in SQLite. But then it shouldn't be trying to POST the object to the server either.

Resources