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

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.

Related

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.

Core Data issue

I implemented core data in my app.
I am fetching data in ViewWillAppear method.
I am assign fetch result to local array.
Now, I make change on TextFiledDidEndEditing method to local array, but not saved to persistence store.
But when I again come to that view & try to re-fretch on ViewWillAppear method then that changed remain as it is.
Help to solve this
Thank you
NSFetchRequest has a property includesPendingChanges, which has a default of true. Just bear this in mind.
NSManagedObjectContext can tell you about insertedObjects, updatedObjects and deletedObjects.
The above all relates to dirty context state - changes you've made but which haven't been saved yet.
So while you aren't saving your change the context is still dirty with the change and each time you make a request the managed objects you get back will have those changes. The only thing you can filter out is inserted or deleted objects by using the fetch request flag (though that isn't your case here).
If you want to get rid of the changes you should use an undo manager, so rollback or reset the context.

RestKit serialization creating duplicate entries

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

Does executeFetchRequest:error: return the same object instance every time?

I'm new to core data and had a query.
If I call executeFetchRequest:error: to retrieve an entity from the context, and store this entity in a variable called A, and I repeat the process and store it the next time in a variable called B, will A and B refer to the same instance of the NSManagedObject i.e. will a change made to object A also be made to object B?
In addition, assuming I proceed to delete the entity from the managed object context, what would happen to these references?
Take a look at the Core Data Programming Guide section on Faulting and Uniquing. To quote:
Uniquing Ensures a Single Managed Object per Record per Context
Core Data ensures that—in a given managed object context—an entry in a persistent store is associated with only one managed object. The technique is known as uniquing. Without uniquing, you might end up with a context maintaining more than one object to represent a given record.
So, provided you execute the fetches on the same context, the returned results will point to the same instances.
When you delete an object, it is flagged for deletion until the next save operation, at which point it is deleted from the store. If you retain a reference to it thereafter, CoreData will throw an error if you try to access it. From the same document, in the section on creating and deleting objects:
You can find out if a managed object has been marked for deletion by sending it an isDeleted message. If the return value is YES, this means that the object will be deleted during the next save operation, or put another way, that the object is marked deleted for the current (pending) transaction.
the NSManagedObject subclasses are created and handled by the NSManagedContext by itself. It means you might possibly get the same instance of the NSManagedObject when you fetch them more time (but you don't know for sure). If you want to compare 2 objects, you should use some of your "isEqualTo:" implementations.
When you delete the object in that context, the contents of your properties will be removed. That means they will be set to nil if they are weak or they will point to deallocated memory if they are string. So it might happen that you app crashes when you try to access them via your properties(I've experienced that :)).
Every time you fetch an object you are obtaining different instances. To check if represents the same object you should compare objectID properties (to check if they are pointing to the same record in the persistent store).
If you delete one from context, you can check if the other is in context using existingObjectWithId:error

RestKit: Managed object mapped inside a plain old Objective-C object

Preamble: I'm using RestKit 0.21.0, but I've also tried 0.23.0.
I have a number of mappings where a plain old Objective-C object (let's call that a POOCO) contains an NSManagedObject. It seems that when doing GET requests on this root POOCO, the managed object is created under the mainQueueManagedObjectContext.
However when I do a PUT against the POOCO, when the response is loaded (the response is a copy of the object that was sent), the managed sub-object is created under a temporary NSManagedObjectContext, whose parent is the mainQueueManagedObjectContext.
A quick aside on re-fetching in RestKit:
If the request were composed entirely of NSManagedObject instances, the resulting objects would be "refetched" using (I think) the mainQueueManagedObjectContext. (IIRC, once all the object mapping is complete, the objects and sub-objects would all be re-fetched from the main MOC, replacing the objects that were created on the temporary MOC used for mapping.)
This refetching is done (as far as I can tell) because when a MOC gets dealloc'd, all of the managed objects that it manages become invalid. So we refetch the objects in a MOC to which myObjectManager.managedObjectStore maintains a strong reference, so that they remain valid after the temporary MOC goes away.
Because the root object in the mapping is a POOCO, any references it has to managed objects do not get refetched, so they remain attached to the temporary MOC, and become invalid once mapping is complete.
Has anyone else run into issues like this? Are there best practices you can suggest? Is it possible to tell RestKit, "refetch these managed objects using the mainQueueManagedObjectContext" or anything like that?
Managed object instances can't be passed between threads, and RestKit does the mapping on background threads. If the returned objects are managed objects then they will be transferred to the main thread for you. It could be argued that nested managed objects not being switched to the main thread for you is a bug / oversight in RestKit (that you could raise as an issue in github).
You can ask RestKit to use the main thread for some things (by explicitly creating the operations and running them, though this doesn't cover everything) but you really don't want to do that.
So, your main option is to have a method in your container object which you can call in the success block and which iterates through the contained managed objects and 'refetches' them - as you know that all the objects exist this can be done with objectWithID:, and the success block is called on the main thread.
The question actually has some misconceptions about when RestKit performs refetching. Under the circumstances described in the question, RestKit actually does return an instance of RKRefetchingMappingResult in the success block. Calling any of the accessors on that object (such as array or firstObject) actually does perform the refetching. The mistake was that I was never invoking any of these methods on the RKMappingResult.
My assumption was that, since the object I was sending in the PUT request would become the operation's targetObject, the object would be updated in-place by RestKit. This is why I never felt the need to do anything with the RKMappingResult. The assumption isn't technically wrong, but by accessing the object directly once the mapping was complete instead of using the RKMappingResult, I was skipping the refetching step.
I've proposed a change in this behaviour in an issue I've filed with the RestKit folks, to make refetching automatic in certain circumstances.
To summarize:
When making a request using RKManagedObjectRequestOperation (whether you specify that manually or RestKit selects this class for you), always make sure you exercise the RKMappingResult to ensure that the results are re-fetched from the correct NSManagedObjectContext.

Resources