core data versioning on document based applications - objective-c

My application uses Core-data. Now, with the release of the new version I need to change the database structure.
I know this is done via versioning, but all I have found, all tutorials are not for document based applications and at some point i get stuck. How can Versioning be implemented on a document based application, where the document is the database itself and can have any name?
Thanks
---Additional info----
what i would need to do is: open the application, hit the "open" button, select the NSManagedDocument from the filesystem. that is my database (can have any name) if on opening it detects that it is an old structure it should update its structure to the current one. (one column added)

It seems to me that the fact that you are wrapping the SQLite store into an NSManagedDocument is irrelevant to the model versioning procedure.
Just proceed by adding the persistent store options in code and the new model version in Xcode.
When setting up your core data stack - i.e. after the document with the DB has been chosen - you have to add these options to the persistent store when creating the persistent store coordinator:
NSString * const NSMigratePersistentStoresAutomaticallyOption;
NSString * const NSInferMappingModelAutomaticallyOption;
As the names of these options imply, it should work automatically from here. The actual call would look something like this:
[persistentStoreCoordinator
addPersistentStoreWithType:NSSQLiteStoreType
configuration:#"Default"
URL:fileURL
options:#{NSMigratePersistentStoresAutomaticallyOption : #(YES),
NSInferMappingModelAutomaticallyOption : #(YES)}
error:&error];

Related

Steps to take in migrating data from Core Data to Realm?

I was reading this article by Tim Oliver (Migrating an App from Core Data to Realm) and I came across the following paragraph, which has been the most guidance I have been able to find as far as converting my app from Core Data to Realm:
"Once you’ve migrated to Realm, you can re-link the Core Data framework back into your app, use raw NSManagedObject objects to fetch your users’ data from Core Data, and then manually pass it over to Realm. You can leave this migration code in your app permanently, or simply remove it after a sufficient period of time has passed."
This seems like a good idea; however, I have not been able to find even one example of how this should be done.
I have already changed all of my Core Data classes over to Realm, so they cannot be used any more. I have the xcdatamodel file available to the app and the core data framework is easily turned back on. Do I need to set up the whole persistent store controller, managedObjectContext, etc. all over again?
While undesirable, that can be done if necessary. Then I am left to figure out, given that 'raw' NSManagedObject will need to be used, how the many-to-many and one-to-many relationships and their inverses will be able to be properly captured.
Can anyone point me in the right direction on this? I am using objective-c but would benefit from examples in Swift if there are any to be pointed to.
I'm the Tim Oliver who wrote that article. :)
So in my own app (for which that article was based on), when I moved from Core Data to Realm, I did a clean break. All of the data I was storing was just cached metadata derived by the document files in the app, so I opted to simply delete the Core Data SQLite file on disk, and then it was only going to be a mild inconvenience the next time the user opened the app as the metadata was re-calculated and stored in Realm.
If you've got data in your Core Data file, then the onerous is on you to perform a one-off migration from Core Data to Realm. It's not recommended to access the raw data from the SQLite file directly, so the only way is to keep your Core Data code around and use it to query and copy the data.
What I meant by 'raw NSManagedObjects' is that since Core Data objects are KVC-compliant, if you've already refactored your model classes into Realm classes, you can access the data straight from the base NSManagedObject class using the KVC methods like -[NSObject valueForKey:].
I briefly tested this in one of the official Apple Core Data sample code projects to confirm it was still working.
Originally, accessing data from a Core Data Book object looked like this:
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
// Configure the cell to show the book's title
Book *book = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = book.title;
}
But once you've refactored your Book class into RLMObject, you can still access data from your Core Data store like this:
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
// Configure the cell to show the book's title
NSManagedObject *book = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [book valueForKey:#"title"];
}
In this way, you should still be able to access the data in your Core Data objects like before, but you're free to use the actual classes for Realm now.
Unfortunately, like you mentioned in the comments, this does mean you need to keep some amount of Core Data code around in order to be able to open the Core Data store and get the data out of it.
But ideally, you should be able to confine all of this code to a single 'migrator' class that only needs to run the first time it detects that the user's data hasn't been copied to Realm yet. And after a sufficient amount of time has passed, you could then consider dropping it completely.

Core Data app does not generate spotlight indexable metadata (or i can not find it)

I have a sandboxed, Mavericks only, Core Data, non-document, Mac application.
For one attribute of one entity I selected „Index in Spotlight“,
and in a second attempt I selected „Index in Spotlight“ and „Store in External Record File“.
Following Apples Core Data Spotlight Integration Programming Guide i am at the first point, Your program:
There are three processes involved in creating and maintaining the
Spotlight index:
Your program. Within your program, the Core Data framework maintains
the persistent store and creates the external record files to trigger
the spotlight indexing
The Spotlight importer. [...]
Core Data External Record daemon. [...]
I assume, now, there must be some place where metadata (that a Spotlight Importer could index) or external record files will be generated if I run the application and add data in it.
I can nowhere find such metadata or external record files. I searched everywhere in- and outside the sandbox container of my application. (Note, i am not trying to build the Spotlight Importer yet — i am merely looking for the metadata to be indexable.)
Where would this spotlight indexable metadata normally be found?
What could be the reasons no spotlight indexable metadata is generated?
The directory for the external records has to be in ~/Library/CoreData (or the equivalent in a sandboxed application). You must create it.
Also don't forget to set the store options for your PersistentStoreCoordinator, in the Application Delegate, like this in - (NSPersistentStoreCoordinator *) persistentStoreCoordinator :
//creating the External Records Directory
error = nil;
NSString *externalRecordsSupportFolder = [#"~/Library/CoreData/YOUR_EXTERNAL_RECORD_DIRECTORY" stringByExpandingTildeInPath];
[fileManager createDirectoryAtPath:externalRecordsSupportFolder
withIntermediateDirectories:YES
attributes:nil
error:&error];
if(error){
[NSApp presentError:error];
}
//options for your main Persistent Store Coordinator
NSDictionary *storeOptions = #{
NSExternalRecordExtensionOption:#"YOUR_EXTERNAL_RECORD_EXTENSION",
NSExternalRecordsDirectoryOption:externalRecordsSupportFolder,
NSExternalRecordsFileFormatOption:NSXMLExternalRecordType
};
Then you pass the storeOptions
NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
if (![coordinator addPersistentStoreWithType:NSXMLStoreType configuration:nil URL:url options:storeOptions error:&error]) {
[[NSApplication sharedApplication] presentError:error];
return nil;
}
External record data is stored in the same directory as your primary Core Data files (i.e. your SQLite file), but in a hidden subdirectory. The naming convention for the subdirectory is .ApplicationName_SUPPORT/_EXTERNAL_DATA .
For non-document-based applications, Core Data creates a directory structure within ~/Library/Caches/Metadata for the application. The directory structure may vary with the OS version, Core Data version, etc. At some level there should be an application-specific directory within that structure, and inside that should be the external records file(s) created by Core Data.
If you cannot find these files, use kqueue events, lsof, or libdispatch to monitor the filesystem for changes while you run your application. You should be able to see what locations on the file system are being accessed easily. If external records files are not being created, or are being created in some new location, that too should be obvious.
Spotlight metadata is not stored in discrete files but in Spotlight's own data. You can inspect the metadata of a file by using the mdls command from a terminal.
Example:
mdls /Applications/Maps.app
You can also use the mdimport command to tell Spotlight to index something on demand.
Example:
mdimport ~/Documents/MyAwesomeStuff
mdimport also has a command line option to use a specific importer rather than a system importer. This can be very useful for development. Both mdls and mdimport will print out help messages detailing these arguments if asked.
In the core data model editor, you can check the spotlight indexing for each individual attribute.
Select the attribute and open the Data Model inspector
Check "Index in Spotlight"

Use RESTKit for two way synchronization

is it possible to use RESTKit for two way synchronization?
I played aroud with RESTKit and CoreDate. Now I can download all data from my REST service and all changes (create/modify/delete objects) in CoreDate will be overwritten by RESTKit.
Now I want to choose between both versions (the local version or the remote version). How do I do this? Is there a way to manipulat the mapping, or something like that?
update:
My app should synchronize after some changes or after a specific delay (not difficult). Every object has a change date. Until now I want to keep the newer one (if they are equal the local one).
I hope RestKit is made for changing the strategy how it merges objects. something like a block I can set, where I get both objects and can return the merged object.
What I got so far:
I load the object via RestKit but do not persist them. Also I setup a CoreData store where I store the local objects. After loading the remote object I start to synchronize my self. First searching for pairs and then decide which to take, delete, create, overwrite, and so on...
But this is a big bunch of work and I think RestKit is doing something similar. Why not simply changing the strategy of RestKit for my requirements.
Well this would be the "syncing down" thing. After this I could write the synchronized data set back to the service. The changes are not very frequently so I will not have to check for new changes.
I hope now it's a little bit clearer
What you really want to do is validate the data coming in.
Since RestKit is using CoreData it automatically uses the validation built into CoreData (see here)
Here is an example that will ensure that the date never gets changed to an earlier one.
- (BOOL) validateChangeDate:(id *)ioValue error: (NSError **)outError {
if ([[*ioValue laterDate:self.changeDate] compare:self.changeDate] == NSOrderedSame)
*ioValue = self.changeDate;
return YES;
}
Note: There may be better/faster ways to test to see if we need to change the date
Note 2: According to the docs, if RestKit sees a value rejected, it fails the entire object, which we don't want, so that's why we always return YES and just change the incoming date
Note 3: We also don't want to change the value unless we have to because of what the CoreData docs say here
You may be able to leverage KVC validation to achieve this as it allows you to check and then edit or deny the change for each object and key. Check these docs.

What's the difference between the 5 Magical Record setup methods?

Can anyone give a description of each of the 5 setup methods?
(void) setupCoreDataStack;
(void) setupAutoMigratingDefaultCoreDataStack;
(void) setupCoreDataStackWithInMemoryStore;
(void) setupCoreDataStackWithStoreNamed:(NSString *)storeName;
(void) setupCoreDataStackWithAutoMigratingSqliteStoreNamed:(NSString *)storeName;
What do they each do and what is the use case for each?
setupCoreDataStack
Use this when you're just getting started with MagicalRecord. This will, as the method states, set up your default Core Data stack. The pieces of the stack are well know and comprise of: NSPersistentStore, NSPersistentStoreCoordinate, NSManagedObjectModel and a default NSManagedObjectContext. At least one of each of these must be instantiated and configured properly for Core Data to work. MagicalRecord provides this single method to configure your stack with a SQLite persistent store located in /Library/Application Support//.sqlite
setupAutoMigratingDefaultCoreDataStack
When you version your model, you will need to migrate your data. This method will do the same as the previous (above) method, but will also enable Auto Migrations.
setupCoreDataStackWithInMemoryStore;
Sometimes, such as when you're writing unit tests, you want your data to go away when your app terminates. This method will also set up a CoreData stack (as mentioned above) but instead of a SQLite store, it creates a persistent store in system memory (RAM).
setupCoreDataStackWithStoreNamed:(NSString *)storeName
Sometimes you want to customize the file name where your data resides. This method does the same as the first, namely setting up the core data stack, and placing a SQLite store in the specific location, but instead of .sqlite, the store is named storeName.sqlite
setupCoreDataStackWithAutoMigratingSqliteStoreNamed:(NSString *)storeName
This does the same as the above method, but also enabled auto migrations. You'll need to do this when you version your model and have a simple migration that simply needs to be enabled.
You can read more on Core Data migrations on Apple's Core Data Reference Documentation

Multiple Contexts for Document-based Cocoa Application?

So I'm building a document-based application in Cocoa and trying to understand NSManagedObjectContext. I've read Apple's "Core Data Basics" but can't wrap my head around it. In my application I created a custom CoreDataUtility class so that all of my classes can get the context via the following lines of code:
MyCoreDataUtility *coreData = [MyCoreDataUtility sharedCoreDataUtility];
NSManagedObjectContext *context = [coreData context];
I got this idea from the site "Cocoa is my girlfriend". Where I get confused is when I run my application and I create a new window (my file owner class is a subclass of NSPersistentDocument) if I make changes to the context on the new window, the view reflects the changes on the old window and as well as the new one. Based on this I'm assuming that both windows are referring to the same context or at least the same data in some way.
So my questions are:
I have the assumption that each time I create a new window a new
context is created, is this correct?
If a new context is created and they are different (I actually
printed out the memory address of the two contexts and they
addresses are different) does this have something to do with the
persistent store coordinator or persistent object store (both of
these concepts are also nebulous in my mind)?
If all documents refer to the same context is it my responsibility to
create a new context every time a document is created?
Thanks all!
I have the assumption that each time I create a new window a new
context is created, is this correct?
No and yes. Creating a new window does not create a new context, but I suppose that what you see as "a new window" is actually your action of "creating a new document". In a document based app with CoreDate you are likely to have a persistent store which creates a new context for every document by itself.
That is also the answer to your second question. The store coordinator is just the central point that manages all documents of your application. It takes care of some menu related actions, such as opening, saving and all side effects.
If all documents refer to the same context is it my responsibility to
create a new context every time a document is created?
That is not possible. One context can be related with only one file (or "store"), hence every document must have "at its minimum" one context. If you are using the template for a CoreData and Multi-document based app, you do not have to worry about creating a new context. As I said before, the store coordinator will take care of that. However as you learn more about CoreData, and specially if you do multithreading, you will have to create more than one context because because a context cannot cross threads.
Could you provide the link from Cocoa is my girlfriend where you took that code? It seems you want a singleton to store your context and usually that is a bad idea in a document based app because it is extremely easy to mess up things giving one function the context of a different document. Your application should have a good flow where the context is passed. However I always use a singleton if my application is not document based.