Versioned Data Model integration in Core Data - ios

I have two versioned Data Models in my iOS project.
The difference between the Data Models is that newer model has a new field named "new_col".
In some part of the project, I need to fetch data filtered by this "new_col" field.
So, I used the following codes.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"new_col = %#", val];
[request setPredicate:predicate];
NSArray *results = [context executeFetchRequest:request error:&err];
And I faced the exception - 'NSInvalidArgumentException', reason: 'keypath new_col not found in entity ...
I know that it's because of the original data model.
I think that I should convert the original data to the new data model.
How can I convert?

If you are adding new attributes and want to maintain backwards compatibility you should specify a default value for the new attribute, or leave it as "optional" in the core data editor.
Once you have done this, you need to make sure automatic migration is enabled. See Implementation of “Automatic Lightweight Migration” for Core Data for details on how to do this. Existing/old entities will then take on the default value (if defined) or nil if the attribute is optional.

Quick Google.
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/Introduction.html
This is supported by Apple and is the correct way to migrate your users data from one model to another.

Related

Questions on core data

I am starting to develop core data database and i do have a few questions that I can,t understand. Can any one please explain in brief and please keep it simple.
1)
NSManagedObject *employee=[NSEntityDescription insertNewObjectForEntityForName:#"Employee" inManagedObjectContext:_managedObjectContext];
[employee setValue:self.empnametextfield.text forKey:#"empname"];
[employee setValue:self.empidtextfield.text forKey:#"empid"];
while saving the data into database into the database i use this code. But why I am creating instances of NSManagedObject & NsentityDescription?
2)
_fetchrequest=[[NSFetchRequest alloc]init];
NSEntityDescription *entity=[NSEntityDescription entityForName:#"Employee" inManagedObjectContext:_managedObjectContext];
[_fetchrequest setEntity:entity];
NSError *error;
_fetchedobjects=[_managedObjectContext executeFetchRequest:_fetchrequest error:&error];
And when i fetch data i use this coding. So my question is why do i use the instance of NSEntity description here?
3)What is the difference between the purposes for which we use "NSEntityDescription" while in saving & fetching data?
Please answer the above 3 questions of mine as I am quite stuck in it?
Thanks in advance.
When you build your core data stack you load a model which describes the data structure objects and relationships. This is built in terms of entity descriptions. They hold the format for the data, the names, types, multiplicities and rules associated. Without this you have no structure, you might as well just have a generic NSSet.
So, when you're doing operations on the data structure, creating new entity instances or querying, you need to get the description of the entity you're working with so the system knows the rules to work with.

Duplicate at the time of insertion using core data

I am inserting values in database using core-data and see that duplicates are also allowed.
I have searched got that i have to fetch the contents first and then match with the current data if not match then insert but it is not seems the feasible solution.
So please give some alternate.
That is correct. Core Data provides an API for managing an object graph. It also provides the ability to persist the object graph.
The programmer must guarantee uniqueness of objects, if such uniqueness is desired.
The usual approach is to have a key field for each object. You can then search for an object with the same key-field value. If such an object is found, you can update the information in that object. If such an object is not found, you can create a new object.
In any case, you, the programmer, are responsible for guaranteeing uniqueness. There are many ways to do this.
Most object representations have an inherent key-field. If your key is the entire object state, then maybe you can add a unique key to the object layout... or use a cryptographic hash of the entire object as a key field.
Try the following
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"MyEntityName"];
[request setPredicate:[NSPredicate predicateWithFormat:#"keyToBeCompared = #"DuplicateString"]];
NSError *error = nil;
MyEntityName *objectInstance = [[self.managedObjectContext executeFetchRequest:request error:&error] lastObject];
if (objectInstance && !error) {
NSLog(#"duplicate object found");
// Jst update the keyValue
objectInstance.keyToBeCompared = #"New Updated value"; // This will automatically update the duplicate value
}

Core Data, iCloud and Pre-Built Data with iOS 7

I'm a in a really common situation mostly driven by inexperience...
My application inserts pre-built data in the DB at the first launch.
I just check for db existence in the device... if it doesn't exist this is the first launch so I add my pre-built data after DB creation.
At the end of the development I decide to activate iCloud, wow! just one row of code to make it works... I tried it on different devices and my pre-built data were obviously duplicated :(
I understood that the better way to manage pre-built data is to work with more than one persistent store, creating read-only store for pre-built data (my pre-built data don't need to be modified by the user, so the read-only solution would be ok).
The problem is that I have relationship between data inserted by user so I can't work on multiple persistent store (core-data doesn't support relation between different stores).
I took a look at Fetched Properties but it seems to require a super drastic edit on my code.
I thought to go with de-duplicating data... so just removing data that are already in the DB, my doubt is that this process is really dangerous for relationship, how can I delete the object that is just a duplicate of the "original" created at the first launch?=
Given that my pre-build data are can't be modified or deleted by the users, which is in your opinion the best way to correct this problem?
// EDIT: Some information about the model:
The entity that users are dealing with (add/edit/modify)represents a TASK.
this entities have a name, and a CATEGORY.
CATEGORY are the entities that I pre-build.
So user add a name for the task and select a CATEGORY from the pre-built data.
In your Category entity, in the PrebuiltData model, create an "id" property (and make sure that this is consistent from version to version) This model should not be managed by iCloud.
In your Task entity, in the UserData model, create a "categoryId" property. This model should be managed by iCloud (or this discussion is meaningless)
Now you can create a method on your Category entity to fetch all the Tasks in the category using:
-(NSArray*)tasks
{
NSFetchRequest* request = [NSFetchRequest fetchRequestWithEntityName:#"Task"];
request.predicate = [NSPredicate predicateWithFormat:#"id = %#", self.id];
return [gTaskManagedObjectContext executeFetchRequest:request error:NULL];
}
Likewise, you can create a method on your Task entity to fetch the category:
-(Category*)category
{
NSFetchRequest* request = [NSFetchRequest fetchRequestWithEntityName:#"Category"];
request.predicate = [NSPredicate predicateWithFormat:#"id = %#", self.categoryId];
NSArray* results = [gTaskManagedObjectContext executeFetchRequest:request error:NULL];
return results.count > 0 ? results[0] : nil;
}

Using Core Data in app without UITableView

I have a simple to do list application. It uses dynamically generated text fields spaced programmatically for the tasks (I didn't use UITableView because of some custom animations and whatnot that I want to use).
In the app the user can create multiple lists (home, work, school, etc.) each with their own tasks.
I want to use Core Data to store the information
Saving the information is straightforward enough but updating my Core Data objects (List and Task) are where I'm getting stuck. Also how, with Core Data, to associate in a specific tasks with a specific list.
let me know if I need to clarify anything.
Your best bet is NSFetchedResultsController. You can use it exactly like in the pattern suggested by the Xcode templates (you can look at it by creating a new project Master-Detail and checking "User Core Data").
You can device your object model (entity Task) with a string attribute for the name as well as a NSNumber boolean attribute for done etc. I also recommend a timestamp and maybe a serial number for ordering it (I find NSOrderedSet unreliable). Your entity List should have a to-many relationship to Task. Pretty straight forward.
List <---->> Task
The only difference is now to find the right object, because you cannot use objectAtIndexPath. You can use the index in fetchedResultsController.fetchedObjects for that. Just make sure your objects are ordered as expected.
I'm not totally clear on your question, however, the task of updating a managed object is straightforward. When you're doing an initial add (similar to an "insert" in SQL) you might use code like this:
NSManagedObject *obj;
obj = [NSEntityDescription insertNewObjectForEntityForName:#"UserData" inManagedObjectContext:context];
[obj setValue:user forKey:#"userName"];
[obj setValue:goalCategory forKey:#"goalCategory"];
[obj setValue:goalDetail forKey:#"goalDetail"];
NSError __autoreleasing error;
[context save:&error];
That's about it for inserting a new item. For updating after you're found the managed object you're working on, you just change the values in the managed object and use [context save:&error]. Here's an example:
UserData *uData = (UserData *) managedObj;
uData.itemName = nameText;
NSError __autoreleasing *error;
[context save:&error];
That's pretty much it.
As to the update, once you have selected the object(s) to be updated, they are contained in
fetchedResultsController.fetchedObjects
which is an NSArray. So, you might do something like this:
UserData *uData = (UserData *) [fetchedResultsController.fetchedObjects objectAtIndex:3];
uData.completed = YES;
NSError __autoreleasing *error;
[context save:&error];
So, this would update the field completed in the UserData entity to be == YES for the object at index 3 in the fetchedObjects array.
I know there are other methods of updating and lots of options but I haven't found any need for them. fetchedObjects is an array containing the items returned by your fetch; to update them, cast each object to the entity (which is defined as a NSManagedObject), make the change then context save..
HTH.
First of all, think is it good idea to use Core Data for your project. If your model is light and simple, maybe it will be better to use plists.
If you choose Core Data, just remember 2 rules:
Each thread owns separate NSManagedObjectContext;
Perform operations with context only in its thread.
And don't worry about optimizations now. Realize any scheme of updating your storage. Make sure it works. And then you should try some other update methods.

RestKit, Core Data, and Relationship problems. Parent object not recognizing child set

I am trying to map a relationship between Articles and the Photos that belong to them using RestKit. Both objects get stored properly when requesting the resource, but it seems the relationship does not persist. In fact, the Article model seems to not even respond to the Photos selector (This may be the 'duh' spot, but I will provide full code to be through).
I've provided all code in a gist, as I find it easier to look through and format then on StackOverflow. Sorry if this is actually an inconvenience.
https://gist.github.com/3733334
And here is the image of the core data model and the relationships set up (sorry, I had to combine them since I can only post 2 hyperlinks currently):
http://imageshack.us/a/img33/5039/stackoverflowissue.jpg
Everything seems to be working properlly except the relationship between the objects when I try to access photos via anArticle.photos. The selector is unrecognized. I set up a convience method in the Photo model to return all photos with a matching article ID, but I feel this is an un-ideal solution as it sort of removes the whole idea of creating a relationship.
I feel there may be something simple I am missing and any help would be greatly appreciated.
Thanks!
So of course it was a "Duh" error. After some help from a local developer, he pointed out that my Photos variable in my Article.h file was an NSArray, and needed to be changed to an NSSet to store objects mapped by RestKit.
Theres some inconsistency between different versions of RestKit. If you are using the latest one mappings should be set up as shown here: https://github.com/RestKit/RestKit/wiki/Object-mapping. If you want to use entity classes for model specific methods make categories on your NSManagedObjects so that when you change your data model you can regenerate them (Do this only after you extract your methods to a category! Select an entity in your .xcdatamodeld and go to Editor -> Create NSManagedObject Subclass...).
I moved my mappings to the controller that is responsible for syncing with the remote API.
This shuld be helpful too: http://andriyadi.me/logging-in-restkit/.
Also Core Data guidelines stress that you should set up inverse relations (but it's not obligatory).
Fetching entities can also be done better in my opinion. In my project I have an NSObject subclass singleton that (among some other Core Data convenience functionalities) fetches by entity and predicate:
- (NSArray *)fetchEntities:(NSString *)entity usingPredicate:(NSPredicate *)predicate {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entity];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *results = [self.managedObjectContext executeFetchRequest:request error:&error];
if (error) {
RLog(#"Error fetching entity %# using predicate %#", entity, predicate);
abort();
}
if ([results count] >= 1) {
return results;
}
return nil;
}
You can alter it to pass predicates as NSStrings too.
Side note:
Currently I'm also struggling with RestKit and object mapping myself :).

Resources