Migration with split core data model - ios

The project(a static library) had a single data model with entities which could actually be grouped by their concerns and put into separate data models but was not separated because necessity did not arise in the beginning when the project was designed. But now as the project has grown and we are refactoring code to modularise it, the data model had to be split into one single core data model and other models belonging to each module.The challenge now is that we are unable to figure out how to safely migrate with this scenario.
To be clear---
In the beginning --- The Project(with no modules)
had single data model called CoreDataModel with entities,
A,B,C,D.
Now ---------------- The Project has 1 module(a separate static library itself with main project lib
as dependency) with the original CoreDataModel split into two 1) CoreDataModel with entities A,B. 2) ModuleDataModel with entities C,D.
Now to safely migrate I want the data existing in current app belonging to the original CoreDataModel(in file CoreData.sqllite) to be separately(with respect to their belonging entities) put into CoreData.sqllite and ModuleData.sqllite.

Reading your first sentence, I am assuming complete separation between your "CoreDataModel" and "ModuleDataModel", i.e. no relationships between (A or B) and (C or D).
I might try to perform the migration with one or more NSEntityMigrationPolicy and a specified NSMigrationManager, using (code fragment):
NSMigrationManager *migrationManager=[[NSMigrationManager alloc] initWithSourceModel: sourceModel destinationModel: destModel];
BOOL success=[migrationManager migrateStoreFromURL: sourceStoreURL type: NSSQLiteStoreType options: nil withMappingModel: mappingModel toDestinationURL: destinationStoreURL destinationType: NSSQLiteStoreType destinationOptions: nil error: &migrationError];
Your migration policies can override:
- (BOOL) createDestinationInstancesForSourceInstance: (NSManagedObject*) instance entityMapping: (NSEntityMapping*) mapping manager: (NSMigrationManager*) manager error: (NSError* __autoreleasing *) error
during which you can store the relevant (C, D) source entities' attributes (and entityType) in a dictionary, add this to an array and store that array in the -[NSMigrationManager userInfo] dictionary. At this stage, you would only actually be migrating A, B entities to your new store.
Once that initial migration is complete (typically when setting up your main / "CoreData" persistentStoreCoordinator) you can retrieve the migrationManager's userInfo dictionary and store your array of dictionaries, e.g. in a property of your app delegate / core data controller.
Once the NSManagedContexts have been initialised, "CoreData" should be properly migrated, but "ModuleData" would be empty. You can then traverse your stored array, creating new entities of C and D and setting their attributes.
I haven't covered the extra complexity of C and D relationships, but hopefully this will be a pointer in the right direction. I did something similar when I wanted to separate a standard MOM/PSC/MOC into one MOM/MOC with two on-disk stores, managed by the same PSC. It worked fine, though seemed a little untidy, but I couldn't solve using migration policies alone. I would be interested if there are better approaches.

Related

Best way to save a dictionary containing bunch of Core Data objects?

I wish to save a dictionary containing some Core Data objects (bunch of different entities). The objects also have quite a few relationships (and inverse relationships) defined. What would be the best way to go about it?
I tried using NSKeyedArchiver and writing to a file. While that works great, when trying to read from the file using NSKeyedUnarchiver, it fails on one of the classes with the error
-[SomeEntity initWithCoder:]: unrecognized selector sent to instance
EDIT - More details
I have a bunch of objects, each having properties based on which they can be filtered. The properties are in themselves Core Data entity objects since they have a complex structure.
I wish to save the filters the user has selected so that the next time they view the objects, the objects can be filtered as per their previous selection.
Say there are 3 filters, Filter A, B and C and each can have 5 different values. Now the user might select Filter A1, A2, B1 and C3 (or a different combination). My question, how do I save these selected filters (A1, A2, B1 and C3 in this case) using Core Data?
Let me see if I understand your question: You have a collection of managedObjects that are already saved in a context. They may already be persisted in the SQL database. You want to save that collection ALSO to another file for other purposes. You have already considered saving the information of this collection inside core-data in some way and have already rejected it. You have also considered simply saving the query generation tokens to save the state of the database as it currently is, but that also is not what you want. The point is to have a file that contains a copy of some of the managedObjects organized in a way that you can get the data back without using the SQL database that was already designed exactly for that purpose.
Solution 1: Turn each managed object in a dictionary.
You can get every attribute and every property of every object by getting a managed object's entity and then accessing the attributesByName and
relationshipsByName property of the entity. From there you make a simple loop to put each property into a dictionary. I also suggest you store the objectID and point to the objectID when encoding the relationships. Then replace the managedObject in your dictionary with dictionary that contains all the attributes and relationship. This new dictionary should be easy to archive and unarchive.
This make sure that the data when you unarchive is exactly how you left it. When you unarchive you will get a COPY of data and if the managed objects have changed in your database since then, you will get the OLD values. Also these copies are not core-data object because they are unconnected to a managed Object Context.
Solution 2: Just save the Managed Object's ObjectId.
Replace every managed object in your collection with the object's objectId. This dictionary can be easily archived. When you unarchive it replace every objectId with a core data object (if found) using existingObjectWithID: on the context. If entities have been deleted then you won't get them back. If entities have changed then you will get the NEW values.
Solution 3: Don't do any of this
It seems to me that you may not be aware core-data are already saved in a database. If you have some collection of managedObjects, you should be able to recreated it from your database. If you aren't able to, then you should add properties and/or relationships that will allow you to so.
Try like this :
ARCHIVE :
NSDictionary *yourDictData = [NSDictionary dictionaryWithObject:json forKey:#"key"]; // This is for example. Here you have to replace ur dictionary
NSData *myData = [NSKeyedArchiver archivedDataWithRootObject:yourDictData];
UNARCHIVE :
NSDictionary *myData = [NSKeyedUnarchiver unarchiveObjectWithData:yourDictData];

What is the way to persist a trie data tree with 27000 nodes in iOS using Swift?

I am building a Trie tree that will have about 27000 of the nodes below. Instead of recreating it every time on app start, I would like to persist. Because the child property is a dictionary to another node, I'm having trouble using NSCoding to archive and store it in the core data entity. Is there a way to store this node in Core Data? Or should I be using a different type of persistence?
class TrieNode {
var letter:Character
var fullWord:Bool
var leadingLetters:String
var child = [Character:TrieNode]()
init (letter:Character, leadingLetters:String, fullWord:Bool) {
self.letter = letter
self.fullWord = fullWord
self.leadingLetters = leadingLetters
}
}
The main problem I had in trying to use Core Data is how to convert var child = [Character:TrieNode]() into NSData or another useable type that CD can store in an entity. Examples on how to do that would be appreciated.
It's a little awkward in Core Data. I think what I'd do is:
Create a new entity called something like TrieNodeLink. It has one property, a string called something like childString and one relationship, called node of type TrieNode. Each instance of this entity represents one single sub-node of a trie node.
Add a new to-many relationship from your existing TrieNode to the new TrieNodeLink entity.
Keep your existing child dictionary. At a convenient time, initialize this dictionary by scanning the new to-many relationship from step 2. A convenient time might be in awakeFromFetch, or else you could make it a Swift lazy property. Or if you want to pre-load data for faster performance at the cost of higher memory use, you might write some code to recursively load child nodes a few levels deep before they're needed.
The effect of this would be that you'd load portions of the trie on demand, when needed. Once loaded you'd be able to use your child dictionary to quickly look up child nodes.

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.

Deleting or removing ManagedObject in CoreData

In the documentation and in the broad literature the generated factory method to delete/remove a subclassed managed object in CoreData for IOS is
(void)removeXXXObject:(NSManagedObject *)value
where XXX is the corresponding relationship or we can use simply removeObject.
In my code I used this:
Data *lastData = [[self sortedPersonDatas] objectAtIndex:0];
[selectedPerson removePersonDatasObject:lastData];
where PersonDatas is a one-to-many relationship to Data managed object from I took the last data (lastData resulted from a sorted array of all data)
But using the first two remove methods and checking the SQL database behind we can find that the actual data is existing just the inverse relationship is null.
To completely delete the data (all attributes and the object) I had to use:
[selectedPerson.managedObjectContext deleteObject:lastData];
The question: which is the better method and is it correct that CoreData leaves the data intact?
removeXXXObject only removes an object from a to-many relationship, but does not delete the object from the store. To do so, you have to indeed use deleteObject - this is the desired behavior. Calling deleteObject will by default also set the corresponding relationships to nil (see https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdRelationships.html#//apple_ref/doc/uid/TP40001857-SW1).

Cross-Store weak relationship with Fetched Properties?

I would like to separate my reference data from my user data in my Core Data model to simplify future updates of my app (and because, I plan to store the database on the cloud and there is no need to store reference data on the cloud as this is part of my application). Therefore, I've been looking for a while for a way to code a cross-store relationship using fetched properties. I have not found any example implementations of this.
I have a Core Data model using 2 configurations :
data model config 1 : UserData (entities relative to user)
data model config 2 : ReferenceData (entities relative to application itself)
I set up 2 different SQLite persistent stores for both config.
UserData config (and store) contains entity "User"
ReferenceData config (and store) contains entities "Type" and "Item".
I would like to create two single-way weak relationships as below :
A "User" has a unique "Type"
A "User" has many "Items"
Here are my questions :
How do I set up my properties?
Do I need 2 properties for each relation (one for storing Unique ID and another to access my fetched results)?
Could this weak relationship be ordered?
Could someone give me an example implementation of this?
As a follow-on to Marcus' answer:
Looking through the forums and docs, I read that I should use the URI Representation of my entity instance instead of objectID. What is the reason behind this?
// Get the URI of my object to reference
NSURL * uriObjectB [[myObjectB objectID] URIRepresentation];
Next, I wonder, how do I store my object B URI (NSURL) in my parent object A as a weak relationship? What attribute type should I use? How do I convert this? I heard about archive... ?
Then, later I should retrieve the managed object the same way (by unconvert/unarchive the URIRepresentation) and get Object from URI
// Get the Object ID from the URI
NSManagedObjectID* idObjectB = [storeCoordinator managedObjectIDForURIRepresentation:[[myManagedObject objectID] URIRepresentation]];
// Get the Managed Object for the idOjectB ...
And last but not least, shouId I declare two properties in my entity A, one for persisting of URI needs and another for retrieving direclty object B?
NSURL * uriObjectB [objectA uriObjectB];
ObjectB * myObjectB = [objectA objectB];
As you can read, I really miss some simple example to implement thes weak relationships ! I would really appreciate some help.
Splitting the data is the right answer by far. Reference data should not be synced with the cloud, especially since iCloud has soft caps on what it will allow an application to sync and store in documents.
To create soft references across to stores (they do not need to be SQLite but it is a good idea for general app performance) you will need to have some kind of unique key that can be referenced from the other side; a good old fashioned foreign key.
From there you can create a fetched property in the model to reference the entity.
While this relationship cannot be ordered directly you can create order via a sort index or if it has a logical sort then you can sort it once you retrieve the data (I use convenience methods for this that return a sorted array instead of a set).
I can build up an example but you really are on the right track. The only fun part is migration. When you detect a migration situation you will need to migrate each store independently before you build up your core data stack. It sounds tricky but it really is not that hard to accomplish.
Example
Imagine you have a UserBar entity in the user store and a RefBar entity in the reference store. The RefBar will then have a fetchedProperty "relationship" with a UserBar thereby creating a ToOne relationship.
UserBar
----------
refBarID : NSInteger
RefBar
--------
identifier : NSInteger
You can then create a fetched property on the RefBar entity in the modeler with a predicate of:
$FETCHED_PROPERTY.refBarID == identifier
Lets name that predicate "userBarFetched"
Now that will return an array so we want to add a convenience method to the RefBar
#class UserBar;
#interface RefBar : NSManagedObject
- (UserBar*)userBar;
#end
#implementation RefBar
- (UserBar*)userBar
{
NSArray *fetched = [self valueForKey:#"userBarFetched"];
return [fetched lastObject];
}
#end
To create a ToMany is the same except your convenience method would return an array and you would sort the array before returning it.
As Heath Borders mentioned, it is possible to add a sort to the NSFetchedProperty if you want but you must do it in code. Personally I have always found it wasteful and don't use that feature. It might be more useful if I could set the sort in the modeler.
Using the ObjectID
I do not recommend using the ObjectID or the URIRepresentation. The ObjectID (and therefore the URIRepresentation of that ObjectID) can and will change. Whenever you migrate a database that value will change. You are far better off creating a non-changing GUID.
The weak relationship
You only need a single value on the M side of the relationship and that stores the foreign identifier. In your object subclass you only need to implement accessors that retrieve the object (or objects).
I would go with just one store.
For storing stuff in the cloud, you will anyway have to serialize the data, either as JSON or SQL statements, or whatever scheme you prefer.
You will need a local copy of the data on the user's device, so he can access it quickly and offline. The cloud store can have only the user entity, while the local store (part of the app) can also have the reference entity.
I have a similar project with a huge reference store (20000 records) with geographic information, and user generated content ("posts"). I use a single store. When I ship the app, the "posts" entity is also defined but empty. When I update the data model I simply re-generate the whole reference store before shipping.
I see absolutely no reason to go for a cross store solution here.

Resources