Magical Record - context (not saving) - ios

I'm completely unsure what's the problem. I'm creating a new object and inserting it into context:
Object *object = [Object MR_createEntity];
object.name = #"blahblah";
NSManagedObjectContext *context = [NSManagedObjectContext MR_defaultContext];
[context MR_saveToPersistentStoreAndWait];
According to the docs I shouldn't use + MR_contextForCurrentThread so I don't:
In particular, do not use +MR_contextForCurrentThread from within any of the +[MagicalRecord saveWithBlock:…] methods — the returned context may not be correct!
However if I open my app data I can see 3 files (1)appname.sqlite, (2)appname.sqlite-shm and (3)appname.sqlite-wal. As far as I know 2&3 are only cache. When I open file 1 accompanied by 2 and 3 I can see all my data. However when I open only file 1 (copied somewhere far from 2&3) there is no data in my database, so I suppouse something isn't working ok.
The problem is visible only on the device not on the simulator and only if you copy .sqlite to your computer from app data. If you copy all 3 files you can see all the records, but it suggest that everything is saved in cache instead of sqlite.
Maybe I should exchange [Object MR_createEntity] to [Object MR_createInContext:context] because the object is created but not inserted into persistent store?

The code you've given here is correct. The shm file is called the Shared Memeory log. And wal is called the Write Ahead Log. They are there to help SQLite perform saves and fetches faster. That said, you should always be able to open up the sqlite file and see whats in the data store.
With that, some things to look at are to enable the logs. Likely your data is not saving because you have specified a property as required and did not fill it in. Also, you may be looking in the wrong place o

Related

Remove core data model on iOS app update

My question is related to migration. I cannot do a lightweight migration as there are a lot of changes with attribute types and new relationships. I don't have time for a heavy weight migration since the code is not mine and needs faster delivery.
The workaround, which could work is when the app is upgraded, app should remove the old data and data model as the data is of no use and can be downloaded from the server again. On the app did finish launching, get the .db URL and just remove it and recreate it for the very first time after the upgrade?
After some research, all the methods are pointed to light weight migration. If there is a better way please assist.
-(void) removeCoreDataAndReset{
NSError *error;
NSPersistentStoreCoordinator *storeCoordinator = storeCordinator;
for (NSPersistentStore *store in storeCoordinator.persistentStores) {
[storeCoordinator removePersistentStore:store error:&error];
[[NSFileManager defaultManager] removeItemAtPath:store.URL.path error:&error];
}
// Initialise managedobjectcontext , Store Coordinator etc
}
Reinitialise all after this method as you do in statrt
To remove the persistent store, you need to remove:
The actual persistent store file. This is located wherever you put it. You tell Core Data where it is when you call addPersistentStoreWithType:configuration:URL:options:error:, so if you're not sure, check there.
The journal files. These will have the same name as the persistent store file, but with -wal and -shm added to the end. This is very important, because in most cases nearly all of the existing data is in these files.
You can remove files with methods on NSFileManager. If you do this, do it before accessing Core Data in any way, i.e. before creating any Core Data objects of any kind.

Core Data fetch predicate nil check failing/unexpected results?

I have a Core Data layer with several thousand entities, constantly syncing to a server. The sync process uses fetch requests to check for deleted_at for the purposes of soft-deletion. There is a single context performing save operations in a performBlockAndWait call. The relationship mapping is handled by the RestKit library.
The CoreDataEntity class is a subclass of NSManagedObject, and it is also the superclass for all our different core data object classes. It has some attributes that are inherited by all our entities, such as deleted_at, entity_id, and all the boilerplate fetch and sync methods.
My issue is some fetch requests seem to return inconsistent results after modifications to the objects. For example after deleting an object (setting deleted_at to the current date):
[CoreDataEntity fetchEntitiesWithPredicate:[NSPredicate predicateWithFormat:#"deleted_at==nil"]];
Returns results with deleted_at == [NSDate today]
I have successfully worked around this behavior by additionally looping through the results and removing the entities with deleted_at set, however I cannot fix the converse issue:
[CoreDataEntity fetchEntitiesWithPredicate:[NSPredicate predicateWithFormat:#"deleted_at!=nil"]];
Is returning an empty array in the same conditions, preventing a server sync from succeeding.
I have confirmed deleted_at is set on the object, and the context save was successful. I just don't understand where to reset whatever cache is causing the outdated results?
Thanks for any help!
Edit: Adding a little more information, it appears that once one of these objects becomes corrupted, the only way get it to register is modifying the value again. Could this be some sort of Core Data index not updating when a value is modified?
Update: It appears to be a problem with RestKit https://github.com/RestKit/RestKit/issues/2218
You are apparently using some sintactic sugar extension to Core Data. I suppose that in your case it is a SheepData, right?
fetchEntitiesWithPredicate: there implemented as follows:
+ (NSArray*)fetchEntitiesWithPredicate:(NSPredicate*)aPredicate
{
return [self fetchEntitiesWithPredicate:aPredicate inContext:[SheepDataManager sharedInstance].managedObjectContext];
}
Are you sure that [SheepDataManager sharedInstance].managedObjectContext receives all the changes that you are making to your objects? Is it receives notifications of saves, or is it child context of your save context?
Try to replace your fetch one-liner with this:
[<your saving context> performBlockAndWait:^{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"CoreDataEntity"];
request.predicate = [NSPredicate predicateWithFormat:#"deleted_at==nil"];
NSArray *results = [<your saving context> executeFetchRequest:request error:NULL];
}];
First, after a save have you looked in the store to make sure your changes are there? Without seeing your entire Core Data stack it is difficult to get a solid understanding what might be going wrong. If you are saving and you see the changes in the store then the question comes into your contexts. How are they built and when. If you are dealing with sibling contexts that could be causing your issue.
More detail is required as to how your core data stack looks.
Yes, the changes are there. As I mentioned in the question, I can loop through my results and remove all those with deleted_at set successfully
That wasn't my question. There is a difference between looking at objects in memory and looking at them in the SQLite file on disk. The questions I have about this behavior are:
Are the changes being persisted to disk before you query for them again
Are you working with multiple contexts and potentially trying to fetch from a stale sibling.
Thus my questions about on disk changes and what your core data stack looks like.
Threading
If you are using one context, are you using more than one thread in your app? If so, are you using that context on more than one thread?
I can see a situation where if you are violating the thread confinement rules you can be corrupting data like this.
Try adding an extra attribute deleted that is a bool with a default of false. Then the attribute is always set and you can look for entities that are either true or false depending on your needs at the moment. If the value is true then you can look at deleted_at to find out when.
Alternatively try setting the deleted_at attribute to some old date (like perhaps 1 Jan 1980), then anything that isn't deleted will have a fixed date that is too old to have been set by the user.
Edit: There is likely some issue with deleted_at having never been touched on some entities that is confusing the system. It is also possible that you have set the fetch request to return results in the dictionary style in which case recent changes will not be reflected in the fetch results.

obtainPermanentIDsForObjects doesn't make passed in object's IDs permanent immediately

I've been wrestling with temporary core data objects within my iOS app for a fair few months now. I use UIManagedDocument which may or may not complicate things a little. The problem I have is when views are trying save URIs for objects during state encoding for restoration I hit problems whenever newly created objects have objectID's that are temporaryIDs.
Previously I'd tried to force save the UIManagedDocument with the following
NSError *saveError=nil;
BOOL bSuccess=[document.managedObjectContext save:&saveError];
[document updateChangeCount:UIDocumentChangeDone];
[document savePresentedItemChangesWithCompletionHandler:^(NSError *errorOrNil)
I thought this was helping fix the temporary objectIDs, it was definitely forcing the saving to store/disk (which shouldn't be necessary when using the more automated UIManagedDocument), but I since discovered that newly created object id's on the document.managedObjectContext were still left with temporary ObjectIDs even after this.
Last night I discovered that the following brute force addition done after the save has occurred in the savePresentedItemChangesWithCompletionHandler's completion handler block seemed to be able to fix up the temporary ObjectIDs that I was still experiencing.
[document.managedObjectContext reset];
This presumably discards the entire context and forces everything to be refreshed with the new permanent ids following the save having completed. I presume this would require at least some form of SQL db being reloaded from disk and so wasn't really an ideal solution.
Finally I discovered that there may be another solution, one that doesn't require brute force saving on the UIManagedDocument, and that's to instead do the following on any newly created NSManagedObject instead
NSError *obtainError=nil;
BOOL bObtainSuccess = [object.managedObjectContext obtainPermanentIDsForObjects:#[object] error:&obtainError];
I do think that this seems to do what's written on the tin. If I test for object's being temporary even just a second or so later then it seems to clear up and find ALL object's processed as permanent. However if I try to test whether they're permanent immediately after calling obtainPermanentIDsForObjects as follows
NSError *obtainError=nil;
BOOL bObtainSuccess = [object.managedObjectContext obtainPermanentIDsForObjects:#[object] error:&obtainError];
assert(![[object objectID] isTemporaryID]);
Then the assert fires, ie. the object still has a temporaryID even though the obtainPermanentIDsForObjects method returned YES, and left obtainError as nil.
This is all done on the main thread, without any context performBlock.. Given the configuration of UIManagedDocument though this should be correct I think.
Has anyone got any thoughts on this? For now hopefully it seems to be ok if i don't check immediately, almost like there's some threading to the operation, which does make me wonder if it should be done on some different thread...
Thanks for your time

link two objects in CoreData

i am new in core data and i created 2 tables,Night and Session. i manage to create new object of Night and new object for Session. when i try this code:
Session * session = [NSEntityDescription insertNewObjectForEntityForName:#"Session" inManagedObjectContext:[[DataManager sharedManager] managedObjectContext]];
Night * night = [NSEntityDescription insertNewObjectForEntityForName:#"Night" inManagedObjectContext:[[DataManager sharedManager] managedObjectContext]];
night.sessions = [NSSet setWithObject:session];
the session is getting into the night and the cool thing is, when i Fetch this night and can get the session for the night using:
currentNight.Seesion
But i can't see this link in the DB tables :(
UPDATE:
I mean when i write night.sessions = [NSSet setWithObject:session]; i need to see in the table DB (yes in the DB.sqlite file).
i thought that i should see some thing there ...
Core Data is not a relational Database.It makes structure of their own.It defines the Database tables structure according to your Managed Objects.For debugging you can see what queries core data is firing on sqlite.This will show you how core data is getting data from these two tables.
You have to go Product -> Edit Scheme -> Then from the left panel select Run yourApp.app and go to the main panel's Arguments Tab.
There you can add an Argument Passed On Launch.
You should add -com.apple.CoreData.SQLDebug 1
Press OK and your are all set.
Than next time it will show all the queries it running to fetch data from your tables.
It's not clear to me what your question is. But:
A context is a scratchpad. Its contents will not be moved to the persistent store until you -save:. If you drop into the filing system and inspect your persistent store outside of your app without having saved, your changes will not be recorded there.
For all of the stores the on-disk format is undefined and implementation dependent. So inspecting them outside of Core Data is not intended to show any specific result.
Anecdotally, if you're using a SQLite store then you should look for a column called Z_SESSIONS or something similar. It'll be a multivalued column. Within it will be the row IDs of all linked sessions. Core Data stores relationships with appropriately named columns and direct row IDs, which are something SQLite supplies implicitly. It does not use an explicit foreign/primary key relationship.
To emphasise the point: that's an implementation-specific of Core Data. It's not defined to be any more reliable than exactly what ARM assembly LLVM will spit out for a particular code structure. It's as helpful to have a sense of it as to know about how the CPU tends to cache, to branch predict, etc, but you shouldn't expect to be able to take the SQLite file and use it elsewhere, or in any way interact with it other than via Core Data.

iOS - Core data - NSManagedObjectContext - not sure if it is saved

Overview
I have an iOS project in which I am using Core data
I am inserting an object, then I want to save it.
I am not sure if save works.
Save seems to be working when app goes into background
When using Simulator, If I click on Stop button on Xcode, save doesn't seem to be working.
Question
Is the save actually happening ?
Am I facing a problem because I created a view based app (the core data checkbox was not available) ?
Steps Followed
I am using the simulator to test it.
Insert an object (code is in the next section)
Save the inserted object (code is in the next section)
I press the Stop button on Xcode to stop running the app
Output noticed
setBeforeSave.count = 1
setAfterSave.count = 0
Before saving, The NSManagedObjectContext method insertedObjects returns 1 object
Before saving, The NSManagedObjectContext method insertedObjects returns 0 objects
When Xcode Stop button is pressed, and when the app is relaunched, the previous data is not available (is it because I clicked on stop on xcode)
managedObjectContext is NOT nil
The NSManagedObjectContext method save: returns YES.
Code to Insert Object
Test *test = [NSEntityDescription insertNewObjectForEntityForName:#"Test" inManagedObjectContext:self.database.managedObjectContext];
Code to Save:
//database is a property of the type UIManagedDocument
NSSet *setBeforeSave = [self.database.managedObjectContext insertedObjects];
NSLog(#"setBeforeSave.count = %i", setBeforeSave.count);
NSError *error = nil;
if(![self.database.managedObjectContext save:&error])
NSLog(#"error = %#", error);
NSSet *setAfterSave = [self.database.managedObjectContext insertedObjects];
NSLog(#"setAfterSave.count = %i", setAfterSave.count);
According to the UIManagedDocument documentation, you should not call save on either of the internal managed contexts. Instead, if you want data saved, you should do one of two things.
Use the undoManager, as it will mark the context dirty, and ready to be saved.
Call [document updateChangeCount:UIDocumentChangeDone];
Thus, in your case, you should replace that save call with:
[self.database updateChangeCount:UIDocumentChangeDone];
And your data will get saved.
EDIT
To provide additional detail. A UIManagedDocument has two MOCs., in a parent/child relationship. The child is the one you get when calling document.managedObjectContext. Now, when a NSManagedObjectContext has a parent, the normal way to propagate changes to the parent is to call save:. However, the UIManagedDocuememt does other stuff, and its documentation specifically says NOT to call save on either the parent or child context.
Well, how does stuff get saved, then? Well, you tell the UIManagedDocument that it is "dirty" and needs to be saved. The two ways you can do that are by either using the undoManager, or calling updateChangeCount:.
When doing either of those, the internals of UIManagedDocument will make sure that the parent context is notified of the change. At some point in the future, the parent will effect the change to the actual backing store (i.e., file(s) on disk).
Furthermore, when a context is "saved" it may or may not keep references to the objects that were changed. You can set a property which tells it to retain objects that have been saved, or to release them.
Hopefully, that addresses your problems.
to summarize, though, see the original answer.
BTW, you can actually see a log of what the SQL store is doing underneath by adding "-com.apple.CoreData.SQLDebug 1" to your command line arguments. You do that in the "Edit Scheme" dialog.

Resources