Core Data with A-Z sections - iphone

I am trying to add an A-Z index for a core-data populated table using the example here: http://blog.sallarp.com/iphone-core-data-uitableview-drill-down/
However, I can't work out where I can set my sectionNameKeyPath property in the core data helper header file:
Do I need to make a NSFetchedResultsController like they do in CoreDataBooks? Or can I just add it in somewhere here?! Sorry for the ignorance, any thoughts / help would be much appreciated (this is day 3 of tearing my hair out over it).
+(NSMutableArray *) searchObjectsInContext: (NSString*) entityName : (NSPredicate *) predicate : (NSString*) sortKey : (BOOL) sortAscending : (NSManagedObjectContext *) managedObjectContext
{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
// If a predicate was passed, pass it to the query
if(predicate != nil)
{
[request setPredicate:predicate];
}
// If a sort key was passed, use it for sorting.
if(sortKey != nil)
{
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:sortKey ascending:sortAscending];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptors release];
[sortDescriptor release];
}
NSError *error;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
[request release];
return mutableFetchResults;
}

You need to implement the appropriate table view datasource methods (sectionIndexTitlesForTableView: and tableView:sectionForSectionIndexTitle:atIndex:).
I would recommend using an NSFetchedResultsController to do this. Check out this answer to Core Data backed UITableView with indexing for example code.

Related

Coredata not working while popping the view controllers

I have 2 viewcontrollers (say A & B) ... I have some records in A now I want to edit the data in the core data.... For that I push the navigation controller to B.. There I edit the data in core data.. Itz working fine.. This the code I use for editing..
NSManagedObjectContext *managedContext = [self managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"CamImage" inManagedObjectContext:managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
// Set the predicate -- much like a WHERE statement in a SQL database
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name == %#", xapp.patientName];
[request setPredicate:predicate];
// Set the sorting -- mandatory, even if you're fetching a single record/object
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
sortDescriptors = nil;
NSError *error;
// Request the data -- NOTE, this assumes only one match, that
// yourIdentifyingQualifier is unique. It just grabs the first object in the array.
CamImage *event = [[managedObjectContext executeFetchRequest:request error:&error] objectAtIndex:0];
[event setMedicineImage:butImage];
[event setMedicineName:text.text];
[event setQuantity:[jst objectAtIndex:0]];
[event setUnit:[jst objectAtIndex:1]];
[event setMeal:meal];
request = nil;
NSMutableArray *mutableFetchResults = [[self.managedObjectContext executeFetchRequest:request1
error:&error] mutableCopy];
if (mutableFetchResults == nil) {
// Handle the error.
}
// Set self's events array to the mutable array, then clean up.
[self setCamImage:mutableFetchResults];
NSLog(#"AddMedicineArray:%#",camImage);
[self.navigationController popViewControllerAnimated:YES];
}
In above code I edited the data and fetched it once again..I can found the data got edited properly.. After editing I pop to B.. In the viewwillappear method I once again fetch the data.. But I find the old records only... Once I run the app again I found the edited data got fetched properly.. Here is the code in viewwillappear
-(void)viewWillAppear:(BOOL)animated{
camImage=Nil;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"CamImage" inManagedObjectContext:self.managedObjectContext];
[request setReturnsObjectsAsFaults:NO];
[request setEntity:entity];
NSError *error = nil;
NSMutableArray *mutableFetchResults = [[self.managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (mutableFetchResults == nil) {
// Handle the error.
}
// Set self's events array to the mutable array, then clean up.
[self setCamImage:mutableFetchResults];
viewWillAppear is not being called, because the parent view remains in the view hierarchy the whole time. Instead, implement navigationController:willShowViewController:animated: See this question: iOS 5 viewWillAppear not being called when popping a NavigationController

How to extract specific attribute from core date store

I'm executing a fetch request with sort descriptor to populate a mutable array as follows:
NSFetchRequest *requestA = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"YearPhoto" inManagedObjectContext:managedObjectContext];
[requestA setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"date" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[requestA setSortDescriptors:sortDescriptors];
[sortDescriptor release];
[sortDescriptors release];
NSError *error = nil;
NSMutableArray *mutableFetchResultsA = [[managedObjectContext executeFetchRequest:requestA error:&error] mutableCopy];
if (mutableFetchResultsA == nil) {
}
[self setImageArray:mutableFetchResultsA];
[mutableFetchResultsA release];
[requestA release];
I need to extract the attribute of YearPhoto, friendsPhoto (UIImage ~60KB stored in core data store) to setImageArray for use here in an animation within a UIImageView.....
self.theImageView.animationImages = [NSMutableArray initWithArray:imageArray];
I have also tried:
self.theImageView.animationImages = [NSArray initWithObjects:yearPhoto.friendsPhoto, nil];
I can't work it out. Checked docs but they are not very clear on this specific problem
Anyone see the error in my ways?
Assuming that "Year Photo" is being returned as an Entity you should take the object out of the mutableFetchResultsA and then lookup the friendsPhoto from that object. I'm also not sure what you datatypes are but I'm assuming friendsPhoto is of same type as ImageArray. It would be something like this:
YearPhoto* photo = [mutableFetchResultsA objectAtIndex:0];
[self setImageArray:photo.friendsPhoto];

Quick Question About Fetch And NSObject

I have the following code that fetches a Session object for a particular Exercise object.
This fetch loads data into my UITableView.
The count is fine, I just need a way to extract the Session.timeStamp property so I can set it to UITableViewCell's textLabel property.
Does anyone know how?
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"name == %#", exercise.name]];
NSEntityDescription *sessionEntity = [NSEntityDescription entityForName:#"Exercise" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:sessionEntity];
NSError *error = nil;
NSArray *results = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
NSMutableArray *mutableSessionArray = [NSMutableArray array];
for (Exercise *ex in results) {
Session *session = [ex exercises];
[mutableSessionArray addObject:session];
}
self.sessionArray = [NSArray arrayWithArray:mutableSessionArray];
Call timestamp property of your mananged object subclass or call valueForKey.

Example of how to make a data factory for core data access in cocoa (iPhone)?

I have been slowly learning iPhone development and seem to keep hitting walls where I can't figure out how to do what I want to do the right way :(
Basically, I want a class that handles all interactions with the data layer, for example, getting a mutable array of some list of objects from the data store.
This is pretty trivial in other languages where you have a garbage collector, but in Objective-C on the iPhone, I'm not sure what to do.
This is an example method on a DataFactory class we were creating. Note the comment on where we are not sure when to release....
- (NSMutableArray*)fetchAllDrivers{
NSMutableArray *results = [[NSMutableArray alloc] init];;
if (self.appDelegate != nil) {
NSManagedObjectContext *context = [self.appDelegate managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Person" inManagedObjectContext:context];
[request setEntity: entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"lastName" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects: sortDescriptor, nil];
[request setSortDescriptors: sortDescriptors];
[sortDescriptors release];
[sortDescriptor release];
NSError *error;
results = [[context executeFetchRequest:request error:&error] mutableCopy];
if (results == nil) {
//something went wrong
}
//Where should this be released??? Certainly not here!
[results release];
[request release];
}
else {
[NSException raise:#"Can't fetch b/c app delgate is nil!" format: #"!!!"];
}
return results;
}
Calling code, related to my comment:
NSMutableArray* arr = [dataFactory fetchAllDrivers];
[arr retain];
//Some code where we use arr
[arr release];
Following naming conventions, your fetchAllDrivers should return an autoreleased object.
- (NSMutableArray*)fetchAllDrivers
{
if (!self.appDelegate) {
// Big Problems Raise exception immediately if you want...
return nil;
}
NSManagedObjectContext *context = [self.appDelegate managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Person" inManagedObjectContext:context];
[request setEntity: entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"lastName" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects: sortDescriptor, nil];
[request setSortDescriptors: sortDescriptors];
[sortDescriptors release];
[sortDescriptor release];
NSError *error = nil;
NSMutableArray *results = [[NSMutableArray alloc] initWithArray:[context executeFetchRequest:request error:&error] copyItems:YES];
if (error) {
// Something went wrong
[results release];
// Error handling code here
[request release];
return nil;
}
[request release];
return [results autorelease];
}
NSMutableArray* arr = [dataFactory fetchAllDrivers];
[arr retain];
//Some code where we use arr
[arr release];
By convention, any object returned from the method of an external object is autoreleased. You don't need to retain them except in properties. If you only using arr in the local scope of the method then you don't need to retain/release it. It is autoreleased and will die after the end of the local scope.
If you need to have arr hang around inside the object. You should store it in a retained property:
#property (nonatomic,retain) NSMutableArray *arr;
... then use it with the self notation to ensure retention:
self.arr=[dataFactory fetchAllDrivers];
... then you need only release it in the class' dealloc method.
Having one object manage your data model is very good idea but it is not a "factory". Objective-c does not use factories like C++ and similar languages. Trying to think in those terms will lead to grief. The object should instead be thought of as a "controller" or "manager".

How to properly configure NSFetchedResultsController

I am placing an NSFetchedResultsController into my code so I get that nice automatic sectioning of my table view data.
So I am running a test to make sure everything works properly. I have a single Book entity in my persistent store. I will first perform the fetch the old way, then I will try to use NSFetchedResultsController. The difference between the 2 blocks of code is just 2 lines.
Code without NSFetchedResultsController:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:kBookEntityName inManagedObjectContext:self.managedObjectContext];
[request setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"title" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
[sortDescriptors release];
//The following 2 lines will be replaced by the NSFetchedResultsController
NSMutableArray *mutableFetchResults = [[[self.managedObjectContext executeFetchRequest:request error:nil] mutableCopy] autorelease];
Book *result = (Book*)[mutableFetchResults objectAtIndex:0];
NSString* title = [result valueForKey:#"title"];
NSString* priority = [result valueForKeyPath:#"priority.name"];
[request release];
Now I substitute in the lines for the NSFetchedResultsController:
NSFetchedResultsController* fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"title" cacheName:#"BookList"];
Book *result = (Book*)[fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
Seems pretty cut and dry. The first code block properly fetches the single Book entity. The code with the NSFetchedResultsController, however, does not. Instead it returns nil.
My question is: Am I properly configuring the NSFetchedResultsController in this example?
(note, the fetchedObjects property of the NSFetchedResultsController is also nil)
I think you still need to tell the NSFetchedResultsController to actually perform the fetch:
NSError *error;
BOOL success = [controller performFetch:&error];
(taken from the example in the NSFetchedResultsController reference)
one other thing that seems odd: do you really want to use "title" as the sectionNameKeyPath? won't that basically create a separate section for each book?
You need to initialize your NSFetchedResultsController only once, as follows.The code assumes
NSFetchedResultsController *fetchedResultsController;
NSManagedObjectContext *managedObjectContext;
are declared in the header of your controller, and that managedObjectContext is already properly initialized.
- (void)viewDidLoad {
[super viewDidLoad];
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
// Handle error
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
NSLog(#"%d objects fetched", [[fetchedResultsController fetchedObjects] count]);
}
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
/*
Set up the fetched results controller.
*/
// Create the fetch request for the entity.
NSFetchRequest *request = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:kBookEntityName inManagedObjectContext:self.managedObjectContext];
[request setEntity:entity];
/* Optional settings
[request setResultType:NSManagedObjectResultType];
[request setIncludesPropertyValues:YES];
[request setIncludesPendingChanges:NO];
[request setReturnsObjectsAsFaults:NO];
*/
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"title" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
[sortDescriptors release];;
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:managedObjectContext sectionNameKeyPath:#"title" cacheName:#"BookList"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[request release];
return fetchedResultsController;
}