Questions on core data - ios

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.

Related

iPad splitview application with core data

I am new in app development and I have really big issues with my split view core data iPad application. Even though I made sure that I have built all core data structure very well, I have problems when I pass objects between view and saving them via core data. Basically my problem is in this way:
I have peopleviewcontroller where I list all people in list. And I have addpersonviewcontroller where I create new person and save it to coredata. However, even though saving new object in new class seems successful in save-error structure, no data is written to core data. I debuted the code and figured out that managedjectcontext of created person is always null. Hence, it is not written to core data.
I have spent really lots and lots of time for solution, including days of reading stackverflow, but no solution.
Any help about this issue is deeply appreciated. Thabk you in advance.
Your Person is a subclass of NSManagedObject. In your AddPersonViewController, you never created the object. You have to use insert that entity into core data before you save it.
I recommend having some store/repository that handles all of this.
For instance, I have this in my code:
- (NSManagedObject *)createManagedObject:(NSString *)entityName
{
NSManagedObject *objectCreated = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:self.managedObjectContext];
[self saveContext];
return objectCreated;
}
I then call it with the entity type and it returns a NSManagedObject. You can then cast it to your Person class with
Person *addPerson = (Person *)managedObject;
Now you can make your changes and save it.
A NSManagedObject must be created by the store before it can be saved. You cannot just new it up and expect it to work.

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.

Versioned Data Model integration in Core Data

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.

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 :).

How to get Core Data object from specific Object ID?

I can easily get an object's ID in Core Data using the following code:
NSManagedObjectID *moID = [managedObject objectID];
However, is there a way to get an object out of the core data store by giving it a specific object ID? I know that I can do this by using an NSFetchRequest, like this:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Document" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(objectID = %#)", myObjectID];
[fetchRequest setPredicate:predicate];
However, I'd like to do it in a way that does not initiate its own fetch request. Any ideas?
You want:
-(NSManagedObject *)existingObjectWithID:(NSManagedObjectID *)objectID
error:(NSError **)error
Fetches the object from the store that has that ID, or nil if it doesn't exist.
(Be aware: there are two methods on NSManagedObjectContext with similar-seeming names that tripped me up. To help keep them straight, here's what the other two do:
-(NSManagedObject *)objectWithID:(NSManagedObjectID *)objectID
...will create a fault object with the provided objectID, whether or not such an object actually exists in the store. If it doesn't exist, anything that fires the fault will fail unless you insert the object first with NSManagedObjectContext's insertObject:. The only use I've found for this is copying objects from store to store while preserving ObjectIDs.
-(NSManagedObject *)objectRegisteredForID:(NSManagedObjectID *)objectID
...will return the object that has that ID, if it has been fetched from the store by this managedObjectContext. If anyone knows what this method is useful for, please comment.)
[eta.: Another important difference between the first method and the other two is that existingObjectWithID:error: never returns a fault; it always fetches the whole object for you. If you're trying to avoid that (e.g. working with an expensive-to-fetch object with a big blob property), you have to be clever with objectWithID: or objectRegisteredForID:, which don't fire faults; or use a properly configured fetch request.]
objectWithID: is the method you are looking for, and it is the recommended way to do this. objectWithID: will efficiently use the NSManagedObjectContext to pull the object only as many levels as needed - unlike some of the other means of doing this. objectWithID: will correctly use in-memory information in parent contexts, the persistent store coordinator, and the persistent store itself before going to the backing storage.
This is covered in depth in the WWDC 2012 session "Core Data Best Practices".
Swift 5 version:
https://developer.apple.com/documentation/coredata/nsmanagedobjectcontext/1506686-existingobject
there are also methods object(with:) or registeredObject(for:). Depending on what you need.

Resources