Core Data Reading Data - objective-c

I am a VERY new beginner to Core Data and I have recently been trying to read and write data. I created an entity named "Person" with the entities "name" and "age". I also have a textfield name "personName" and a textfield named "personAge".
- (IBAction)readData:(id)sender
{
NSNumber *ageNum = [NSNumber numberWithInteger:personAge.text.integerValue];
Person *newPerson = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:managedObjectContext];
newPerson.name = personName.text;
newPerson.age = ageNum;
NSLog(#"Person %# name is %#", personName.text, ageNum);
}
When I load the app, all i get is SIGABRT. Even when all I put in the method is
Person *newPerson = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:managedObjectContext];
All help is appreciated.

For Adding values to the core data you can do so:-
- (IBAction)save:(id)sender {
NSManagedObjectContext *context = [self managedObjectContext];
// Create a new managed object
NSManagedObject *person = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:context];
[person setValue:self.personName.text forKey:#"name"];
[person setValue:self.personAge.text forKey:#"age"];
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
}
For fetching the values from core data:-
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Person"];
self.personValues = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
here personValues is a NSMutableArray.
For better understanding about these please go through this link.
http://www.appcoda.com/introduction-to-core-data/
Hope this will help you out.

If you are very new to Core Data, try using the MagicalRecord library which provides a series of helper categories for dealing with a lot of the boiler plate setup of Core Data.
Which brings me to the question you are asking: are you sure your Core Data stack is being setup correctly? Check to make sure your Managed Object Context is valid along with a proper Persistent Store Coordinator.
Best thing to do: put a breakpoint during the Core Data stack setup and step through it making sure everything is setup properly. Or install MagicalRecord and do one method call to [MagicalRecord setupAutomigratingCoreDataStack]...

Related

cannot bind array controller to a nested one to many relationship

I have 3 entities in my data model which are connected as follow:
User<-->>Performance<-->>Trials
Meaning that every user has several performances, in each he/she goes under several trials.
For each entity, I have one table view and one array controller object. For all, I have bound their Managed Object Context parameter to App Delegate.
Then I bound PerformanceArrayController to UserArrayController Content Set (Controller Key: selection and performances relationship). And the same for TrialArrayController: I bound it to PerformanceArrayController (on selection) and trials relationship.
I have no problem in binding single columns of User table view and performance table view to entities attributes. but when I want to do the same for trial table view, first I don't get autocompletion and second when I write the name of the attributes manually, I get a gray exclamation mark. and only the first trial is saved this way but not the rest of them.
Here is my function for inserting into Trial:
- (void) insertIntoTrial: (NSString *) result
{
NSManagedObjectContext *context = [self managedObjectContext];
NSError *error;
Trial *trial = [NSEntityDescription insertNewObjectForEntityForName:#"Trial" inManagedObjectContext:context];
trial.result = result;
trial.time = [NSNumber numberWithDouble:[cueTimestamp timeElapsedInSeconds]];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Performance" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"user.fName==%#", userName]];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (Performance *performance in fetchedObjects) {
[performance addTrialsObject:trial];
trial.performance = performance;
}
if (![context save:&error]) {
NSLog(#"couldn't save Trial info: %#", [error localizedDescription]);
}
}
Thanks in advance,

Deleting Information from Core data

I have a app that parses all my data from my web service as soon as the app is loaded however I want to add an IBACTION (Button) that will delete all of the data from the coredata and reparse the information (therefore updating the data base) - like a Refresh Button.
The Method that call the parsing is:
- (void)getRssData
{
self.webServiceAndParser = [[LukesParser alloc] initWithDelegate:self];
[self.webServiceAndParser getAllData];
}
The core data method im using the build the object in my fetch results controller is:
- (void)insertNewObject
{
// Create a new instance of the entity managed by the fetched results controller.
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
[newManagedObject setValue:[NSDate date] forKey:#"timeStamp"];
// Save the context.
NSError *error = nil;
if (![context save:&error])
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
any ideas?
If you're deleting all the data in your Core Data persistent store, then see Delete/Reset all entries in Core Data? for some possible approaches.

How to use a stored FetchRequest with NSFetchedResultsController?

NSFetchedResultsController provides a lot of nice stuff free when working with tableViews. I also understand that storing fetch requests in the model is good form. Can I use both of these together for super core data goodness? If so, how so?
This example from Mr. Zarra's excellent book shows the template returning an array and I've failed in my attempts to get a fetchedResultsController back.
- (NSArray*)retrieveBigMeals {
NSManagedObjectContext *moc = [self managedObjectContext];
NSManagedObjectModel *mom = [self managedObjectModel];
NSFetchRequest *request = [mom fetchRequestTemplateForName:#"bigMeals"];
NSError *error = nil;
NSArray *result = [moc executeFetchRequest:request error:&error];
if (error) {
[NSApp presentError:error]; return nil;
} return result;
Just use the ios navigation based application template with core data. They already have the code for using NSFetchedResultController there.

Core Data & Generating Model Entities

Standard newbie question. I've created a data model for an iOS application. I am able to create, update and delete entities within the model from various views by using the NSEntityDescription object.
Say if I had a mutable array of objects returned from a fetch request. How can I loop through each one when I do not have a generated object definition from the entity model? By generated object definition I mean, a header and body class definition of the entity described in the data model package.
All CoreData entities derive from NSManagedObject and all the database data from those can be accessed via key value encoding. The minimum you need to know can be gained from the Model. You don't necessarily require the headers.
For example an entity PersonEntity which has a relationship to NameEntity with attribute firstname
NSArray *results = [managedObjectContext queryEntityForName:#"PersonEntity" predicateFormat:nil argumentArray:nil];
for(NSManagedObject *object in results)
{
NSString *name = [object valueForKeyPath:#"nameobject.firstname";
[self doSomething:name];
}
queryEntityForName is my own category. You might find it useful.
#implementation NSManagedObjectContext(VMQueryAdditions)
-(NSArray *)queryEntityForName:(NSString *)name predicateFormat:(NSString *)pstring argumentArray:(NSArray *)arr
{
NSEntityDescription *entity = [NSEntityDescription entityForName:name inManagedObjectContext:self];
NSFetchRequest *fetch = [[[NSFetchRequest alloc] init] autorelease];
[fetch setEntity:entity];
NSPredicate *pred;
if(pstring)
{
if(arr) pred = [NSPredicate predicateWithFormat:pstring argumentArray:arr];
else pred = [NSPredicate predicateWithFormat:pstring];
[fetch setPredicate:pred];
}
NSError *error = nil;
NSArray *results = [self executeFetchRequest:fetch error:&error];
if (error) {
NSLog(#"MOC Fetch - Unresolved error %#, %#", error, [error userInfo]);
return [NSArray array];
}
return results;
}
#end

Adding unique objects to Core Data

I'm working on an iPhone app that gets a number of objects from a database. I'd like to store these using Core Data, but I'm having problems with my relationships.
A Detail contains any number of POIs (points of interest). When I fetch a set of POI's from the server, they contain a detail ID. In order to associate the POI with the Detail (by ID), my process is as follows:
Query the ManagedObjectContext for the detailID.
If that detail exists, add the poi to it.
If it doesn't, create the detail (it has other properties that will be populated lazily).
The problem with this is performance. Performing constant queries to Core Data is slow, to the point where adding a list of 150 POI's takes a minute thanks to the multiple relationships involved.
In my old model, before Core Data (various NSDictionary cache objects) this process was super fast (look up a key in a dictionary, then create it if it doesn't exist)
I have more relationships than just this one, but pretty much every one has to do this check (some are many to many, and they have a real problem).
Does anyone have any suggestions for how I can help this? I could perform fewer queries (by searching for a number of different ID's), but I'm not sure how much this will help.
Some code:
POI *poi = [NSEntityDescription
insertNewObjectForEntityForName:#"POI"
inManagedObjectContext:[(AppDelegate*)[UIApplication sharedApplication].delegate managedObjectContext]];
poi.POIid = [attributeDict objectForKey:kAttributeID];
poi.detailId = [attributeDict objectForKey:kAttributeDetailID];
Detail *detail = [self findDetailForID:poi.POIid];
if(detail == nil)
{
detail = [NSEntityDescription
insertNewObjectForEntityForName:#"Detail"
inManagedObjectContext:[(AppDelegate*)[UIApplication sharedApplication].delegate managedObjectContext]];
detail.title = poi.POIid;
detail.subtitle = #"";
detail.detailType = [attributeDict objectForKey:kAttributeType];
}
-(Detail*)findDetailForID:(NSString*)detailID {
NSManagedObjectContext *moc = [[UIApplication sharedApplication].delegate managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription
entityForName:#"Detail" inManagedObjectContext:moc];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"detailid == %#", detailID];
[request setPredicate:predicate];
NSLog(#"%#", [predicate description]);
NSError *error;
NSArray *array = [moc executeFetchRequest:request error:&error];
if (array == nil || [array count] != 1)
{
// Deal with error...
return nil;
}
return [array objectAtIndex:0];
}
Check out the section titled "Batch Faulting" on the page titled "Core Data Performance" in Xcode's Core Data Programming Guide that Norman linked to in his answer.
Only fetching those managedObjects whose ids are IN a collection (NSSet, NSArray, NSDictionary) of ids of the objects returned by the server may be even more efficient.
NSSet *oids = [[NSSet alloc] initWithObjects:#"oid1", #"oid2", ..., nil];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"oid IN %#", oids];
[oids release];
UPDATE: I worked this tip into a solution for the acani usersView. Basically, after downloading a JSON response of users, the iPhone uses the popular open source JSON framework to parse the response into an NSArray of NSDictionary objects, each representing a user. Then, it makes an NSArray of their uids and does a batch fetch on Core Data to see if any of them already exist on the iPhone. If not, it inserts it. If so, it updates the ones that do exist only if their updated attribute is older than that of the one from the server.
I've gotten all this to work really well, thanks to Norman, who put me on the right path. I'll post my helper class here for others.
Basically, my helper class will look up if an NSManagedObject exists for some ID, and can create it for some ID. This executes quickly enough for me, with 1,000 find/create operations taking around 2 seconds on my iPhone (I also did a few other things there, pure find/create is likely faster).
It does this by caching a dictionary of all the NSManagedObjects, and checking that cache rather than executing a new NSFetchRequest.
A couple of modifications that could help things speed up even further:
1. Get only selected properties for the NSManagedObjects
2. Only get the identifier property for the NSManagedObject into a dictionary, instead of the whole object.
In my performance testing, the single query wasn't the slow part (but with only 1,000 items, I'd expect it to be fast). The slow part was the creation of the items.
#import "CoreDataUniquer.h"
#implementation CoreDataUniquer
//the identifying property is the field on the NSManagedObject that will be used to look up our custom identifier
-(id)initWithEntityName:(NSString*)newEntityName andIdentifyingProperty:(NSString*)newIdProp
{
self = [super init];
if (self != nil) {
entityName = [newEntityName retain];
identifyingProperty = [newIdProp retain];
}
return self;
}
-(NSManagedObject*)findObjectForID:(NSString*)identifier
{
if(identifier == nil)
{
return nil;
}
if(!objectList)
{
NSManagedObjectContext *moc = [(AppDelegate*)[UIApplication sharedApplication].delegate managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription
entityForName:entityName inManagedObjectContext:moc];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];
NSError *error;
NSArray *array = [moc executeFetchRequest:request error:&error];
objectList = [[NSMutableDictionary dictionary] retain];
for (NSManagedObject* p in array) {
NSString* itemId = [p valueForKey:identifyingProperty];
[objectList setObject:p forKey:itemId];
}
}
NSManagedObject* returnedObject = [objectList objectForKey:identifier];
return returnedObject;
}
-(NSManagedObject*)createObjectForID:(NSString*)identifier
{
NSManagedObject* returnedObject = [NSEntityDescription
insertNewObjectForEntityForName:entityName
inManagedObjectContext:[(AppDelegate*)[UIApplication sharedApplication].delegate managedObjectContext]];
[returnedObject setValue:identifier forKey:identifyingProperty];
[objectList setObject:returnedObject forKey:identifier];
return returnedObject;
}
- (void) dealloc
{
DESTROY(entityName);
DESTROY(identifyingProperty);
[super dealloc];
}
#end
This page provides some help on optimizing performance:
http://developer.apple.com/documentation/Cocoa/Conceptual/CoreData/Articles/cdPerformance.html#//apple_ref/doc/uid/TP40003468-SW1
While not very efficient, why not just build them in-memory with a NSDictionary? Read everything from Core Data into a NSDictionary then merge in your data, replacing everything in Core Data.