Fetch data from context that is not yet committed. CoreData - ios

I have created multiple instances of an NSManagedObject entity(for example Car: NSManagedObject) in the default NSManagedObjectContext using MagicalRecord.
I didn't save the context. Is there a way to execute a fetch request and obtain the data that is already in persistent state and the data not yet committed that was added in default context ?

Yes, it is fetched. Please check Apple docs at https://developer.apple.com/reference/coredata/nsmanagedobjectcontext:
An object that meets the criteria specified by request (it is an instance of the entity specified by the request, and it matches the request’s predicate if there is one) and that has been inserted into a context but which is not yet saved to a persistent store, is retrieved if the fetch request is executed on that context.

not as far as I know... you can fetch it all and then look at the objects objectID to decide which one was already saved.
id all = [ctx fetch..];
id savedOnly = [NSMutableArray array];
for(id o in all) {
if([[o objectID] isTemporary] == NO) {
[savedOnly addObject:o];
}
}
OR change your code to use to contexts -- that may be better :D
OR maybe use a predicate like:
savedOnly = [ctx fetchWithPredicate:#"... self.objectID.isTemporary=NO"];
MIGHT work... don't know

If we are talking about the same managed object context - yes, you should be able to get also those objects, which are not committed. This is the default behavior. The managed object context contains also the uncommitted objects, i.e. the objects which are not saved yet in the persistent store.

Related

Is there a way to save NSManagedObjects 1 at a time

I'm facing this problem where I have an NSMutableArray containing NSManagedObjects.
And I want to iterate each one NSManagedObject, set a value to a variable and then save them.
The problem is that I have a verification to see if the objects are in Database or not, and after I save my first NSManagedObject to the database all the others NSManagedObjects that are in that array are also inserted into the database...
Here's a piece of code that describes what is my issue:
for (Category* c in categories) {
Category* original = [Database getCategoryByID:c.categoryid];
// After the first save this will have value because it saves all the objects
// that are in categories Array and the version will be the same...
if (original != nil && original.version >= c.version) {
// This object is already up to date so do not make any changes
} else {
// This object is not up to date, or it does not exist
// update it's contents
c.name = name;
[[c managedObjectContext] MR_saveToPersistentStoreAndWait];
// Here it saves all the objects instead of only 1 object
}
}
Is there a way to only save 1 object at a time while having other NSManagedObjects inside an NSMutableArray ?
With Core Data, you tell a context to save, and it saves everything it has. There's no way to save just one object unless that object is the only one in a context with changes.
In your case, it looks like your array of Category objects are managed objects and that they belong to a managed object context. The easiest way to avoid unexpected saving is to move the save command until after the loop. When you reach that point,
Any Category that needed to be created or updated is ready to save
Any Category that didn't need to be updated has no changes, so it won't be affected by saving the context.
So it should be safe to save everything in the context then. If it has changes, it gets updated, and if it doesn't, it won't change.

Clearing a context in Core Data: reset vs deleting registered objects?

I was looking for posts regarding to this, but I don't fully understand... What is the difference between:
[context reset];
and:
for (NSManagedObjectID *objId in objectIds) {
[context deleteObject:[context objectWithID:objId]];
}
Or are they equivalent?
Thanks
Using reset puts the managed object context back to the state it was in when you first created it-- before you had performed any fetches, created any new objects, etc. If you have any managed objects in memory that were fetched from this context, they're now unusable. Using reset does not affect the persistent store file. All instances still exist afterward, they're just not in memory. They can be fetched again.
Using deleteObject removes the object from the persistent store. It does not exist any more. It can't be fetched anymore because it doesn't exist.

NSIncrementalStore called with NSSaveChangesRequest.deletedObjects == nil

I am in the process of writing an NSIncrementalStore that is backed by a REST service. It works nicely for POSTing and GETing objects, but when DELETEing them I encounter the following problem:
test calls [context delete: object];
context.deletedObjects contains object (as one would expect)
test calls [context save: &error];
iOS calls NSIncrementalStores executeRequest:withContext:error: with a NSSaveChangesRequest; when the call arrives both context.deletedObjects == nil and saveChangesRequest.deletedObjects == nil, so my code cannot find out which objects to DELETE from the server.
How can it happen that deletedObjects is set to nil between the call to the context's save and its invocation its NSIncrementalStore?
UPDATE possible cause: object.objectID is still a temporaryID following save. The NSIncrementalStore is currently obtaining object IDs from newObjectIDForEntity:referenceObject: and the description from Apple's documentation somehow does not seem to apply yet:
New objects inserted into a managed object context are assigned a
temporary ID which is replaced with a permanent one once the object
gets saved to a persistent store.
The trouble was that I did not yet store enough information on the server to recreate (permanent) object IDs during fetches.
This solves the problem in NSIncrementalStore:
When executeRequest:withContext:error: receives an NSSaveChangesRequest it extracts (in my case) reference strings from saved objects with referenceObjectForObjectID: and stores these (along with objects' other attributes) on the server.
When executeRequest:withContext:error: receives an NSFetchRequest it obtains reference strings (along with objects' other attributes) from the server and recreates objects with [context objectWithID: [self newObjectIDForEntity: entity referenceObject: referenceString]].

About the benefit of objectWithID:

The doc says:
If the object is not registered in the context, it may be fetched or
returned as a fault. This method always returns an object. 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). The benefit of this
behavior is that it allows you to create and use faults, then create
the underlying data later or in a separate context.
I'm thinking about the last sentence:
The benefit of this behavior is that it allows you to create and use faults, then create the underlying data later or in a separate context.
Does it mean I can use objectWithID: with an arbitrary ID to get a fault handle of an non-existing object first then later create the object with ID? But how can I assign an arbitrary ID to the new object?
In general, Yes you can get a handle to a non existing item an later create that item.
But, since you don't know what ID will be assigned to the item these is not very useful in that case.
You could use obtainPermanentIDsForObjects:error: to obtain the object final ID, but, this is a trip to the store, and will have a performance penalty.
You can use objectWithID: to "warm up" the coordinator cache. in this manner you may fetch objects in the background, and use this method in another context then access these items without hitting the store (much better performance).
Since every NSManagedObjectID must initially come from a fulfilled NSManagedObject and there is no way to create one from scratch, the only possible way to "create the underlying data later" is meaningless, as follows:
NSManagedObjectID *objID = object.objectID;
[moc deleteObject:object];
…
object = [moc objectWithID:objID]; // Deleted so non-existing
[moc insertObject:object]; // Kinda of resurrecting the deleted object, but not really since the data are gone only ID is left. So it is creating a new object with the old ID. But what's the point?
// Fill data into object
…
[moc save:NULL];
If you use -objectWithID:, it will return a fault if the object is not already registered in the managed object context (ie. only if the object hasn't already been fetched and hasn't been faulted in). In the case that it does return a fault, you do not need to do anything to "create the object". Simply accessing the attributes of the object will automatically fire the fault and let you access its data. There is no additional work needed on your part to create additional objects.

Object returned from fetch request has incorrect data

I am attempting to get an object from a fetch request, and I am checking a property of that object.
Depending on what type the property is will cause me to display a notification or not. After making a successful connection, I set the property type of my object to 'updated' from 'inserted.' Then, when I refresh my view, I pull all objects from coredata and check their properties for the 'updated' type. The problem I am having is that the objects returned in my fetch request that I just attempted to change to 'updated' still display the old 'inserted' value from the fetch request, but don't immediately after the submission. Its like they are reverting. (and I AM saving the context)
What is even more confusing is I have gotten a program to look at the actual tables in the database file stored on the device, and it actually shows the correct value of updated in the table. But the fetch request still comes back with the object having incorrect data. And no amount of refreshing fixes the issue.
How can a fetch request be giving me objects with old/incorrect data when the coredata file shows the tables with correct values?
// code for the fetch request
// return an array of all assets for a specific customer
NSFetchRequest *fetchReq = [NSFetchRequest fetchRequestWithEntityName:#"Asset"];
fetchReq.predicate = [NSPredicate predicateWithFormat:#"customerID = %#" argumentArray:[NSArray arrayWithObject:customerID]];
NSArray *results = [[CoreDataManager sharedManager] executeFetchRequest:fetchReq];
return results;
//executeFetchRequest method
NSManagedObjectContext *context = [self managedObjectContextForCurrentThread];
NSArray *results = [context executeFetchRequest:fetchRequest error:&error];
return results;
#Arcanfel's suggestion (in comments to the original question) certainly helped me find the solution to my own problem, but it was hidden in the 'Show more comments' section.
It would have been helpful to see that as an answer, not a comment, so I've taken his comment and expanded it a little.
Original comment:
[self managedObjectContextForCurrentThread] - I think this can cause a
problem, as different contexts can have different version of
NSManagedObjects. You can call [fetchReq
setShouldRefreshRefetchedObjects:YES] and it will return the most
up-to-date version of your objects
Whilst using the setShouldRefreshRefetchedObjects:YES didn't resolve my problem of subsequent fetch requests not bringing back the right data, it did make me look at my context management.
In one method I was setting the context, and then reading data. In another method I was setting another context, reading data, altering it and then saving that context... a different context to what my first method had.
In short, be careful that you're referring to the same context or anything you update in context1 won't be in synch with what you retrieve from context2.

Resources