I'm selecting a row from a table. I want to pass the results of that selection to a UILabel on a new View. Do I need an NSFetchRequestController subroutine for the below? I wanted a simpler way to pass the event core data selection to a non-UITableView Controller (just a regular UIViewController).
The 'request' at objectIndexPath below is causing the error.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ReviewController *reviewViewController = [[ReviewController alloc] init];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Event" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
Event *selectedEvent = (Event *)[request objectAtIndexPath:indexPath];
reviewViewController.event = selectedEvent;
[self.navigationController pushViewController:reviewViewController animated:YES];
[reviewViewController release];
}
You don't need a new fetched results controller. You just pass the managed object associated with the selected tableview row to the next controller. You are getting the error because you haven't performed a fetch and in any case a fetch request does not have a objectAtIndexPath method.
If you have a fetched results controller for the tableview, you find the selected object with:
reviewViewController.event =[[self.fetchedResultsController fetchedObjects] objectAtIndex:index.row];
Related
Im using a NSManagedObject as an attribute within my ViewController, declared like this:
#property(retain, nonatomic)NSManagedObject *person;
Im propagating the content of a fetch to a UITableView, when the user taps the contact he is looking for, this is what happens:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
self.person = [contacts objectAtIndex:indexPath.row];
Contacts contains the result of the fetch, which is done like this:
NSArray *contactsArray;
NSError *error;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSManagedObjectContext *context = [(OAuthStarterKitAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Contacts" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
testForTrue = [NSPredicate predicateWithFormat:#"SOME CONDITION"];
[fetchRequest setPredicate:testForTrue];
[fetchRequest setFetchLimit:10];
contactsArray = [[NSArray alloc]initWithArray:[context executeFetchRequest:fetchRequest error:&error]];
When the user taps the contact, self.person has that value, but when I try to use that value in another method it's nil, and the address is 0x000000.
This only happens on iOS 5, on iOS 6 person has the value of the contact selected and I can use elsewhere.
Any ideas?
Thanks.
Instead of getting the NSManagedObject, which is always dependant of the life cycle of the context I just stored de NSManagedObjectID and then get the NSManagedObject by using the function objectWithID. And it worked!
First let me tell you what im trying to do. Load data into array(from core data entity), populate table view,
if user wants, reorder cells and update the array.
Thats it.
I have found my problem, i just dont know how to fix it:
I am loading my Entities data/attributes into an array and populating my tableview with the data
(BELOW BEHOLDS THE PROBLEM):
-(void)viewWillAppear:(BOOL)animated{
if (self.context == nil)
{
self.context = [(RootAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
NSFetchRequest *request = [[NSFetchRequest alloc]init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"HandgunAmmo" inManagedObjectContext:self.context];
[request setEntity:entity];
NSError *error;
//PROBLEM!!! the 2 lines below this.
NSMutableArray *array = [[self.context executeFetchRequest:request error:&error] mutableCopy];
[self setTableArray:array];
[self.ammoTable reloadData];
[super viewWillAppear:YES];
}
SO at this point the table view is loaded with data (accounting for cellForRow being called)
The user moves a few cells around, and i update the array as follows:
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
// fetch the object at the row being moved
NSString *r = [self.tableArray objectAtIndex:fromIndexPath.row];
// remove the original from the data structure
[self.tableArray removeObjectAtIndex:fromIndexPath.row];
// insert the object at the target row
[self.tableArray insertObject:r atIndex:toIndexPath.row];
[self.ammoTable reloadData];
}
As you can see the code for reordering the array should work.
But, in the viewWillAppear method, I am loading the entities attributes into the array again and using it to populate the table view which is the problem. When i update the array, its not updating the order of the objects inside of the entity. Does anyone know how to update that? I would really appreciate it!
Thank you!
The managedObjects represented in the array have no sense of their position in the array. Therefore rearranging their place is changing their visible position but not their position in the database.
If you want to sort then you need to do some things:
Have your NSFetchRequest include an NSPredicate that sorts on a sort field
Have your moveRowAtIndexPath method not only reposition the data but also update the sort field to reflect their new position
Save the updated records to the database so that the next fetch will have the correct sort.
If you already have a fetchResultsController you can forgo the array and just use:
NSManagedObject *ammo = [fetchedResultsController objectAtIndexPath:fromIndexPath];
To get an reference to the current object.
I'm using Xcode 4.2 and iOS SDK 5.0 and Apple's Master-Detail Application Template for iPad with Core Data. It's similar to the "Locations" sample code. I've successfully managed to pass the managed object context (MOC) from the MasterViewController (MVC) to the DetailViewController (DVC). The detail view controller accepts input from the user in some text boxes and stores that in core data; this part works like a charm. Now, I have a ActionViewController (AVC) that is a Popover View that is supposed to allow the user to e-mail all of the data in the MOC if they choose so. However, when trying to do a fetch I get a SIGABRT. I used breakpoints to pinpoint exactly where:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Fetch the crosses
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Event"
inManagedObjectContext:context];
[fetchRequest setEntity:entity]; // Where it crashes
//Other code
}
Also, in the debugger I see that the *context pointer is 0x0 which I'm guessing means that it's not there?
The way I made the MVC pass the MOC was like this:
// Pass the managedObjectContext to the DetailViewController as a property
_detailViewController.managedObjectContext = self.managedObjectContext;
//Pass the MOC to the actionViewController
_actionViewController.managedObjectContext = self.managedObjectContext;
I've looked at other posts and it seems that passing the MOC to all the view controllers from the AppDelegate may be the way to go but I wanted to find out why the data input works for the DVC but crashes with fetching data in the AVC. What grand error have I made?
Fixed link to github project: https://github.com/scottdaniel/fly_punnett
So I figured it out, though I'm probably breaking some "good programming" maxim...
All I did was delete
//Pass the MOC to the actionViewController
_actionViewController.managedObjectContext = self.managedObjectContext;
along with all other connections from AVC to MVC (i.e. #class ActionViewController, #property *ActionViewController in the MCV interface) << ask me for the full details if you want them or check it out on github (link above)
Then I just added
#import AppDelegate.h
to my ActionViewController.m
and modified the MOC creation like so:
-(void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Fetch the crosses
NSManagedObjectContext *context = [(AppDelegate *)[[UIApplication
sharedApplication] delegate]
managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Event"
inManagedObjectContext:context];
The MOC is created successfully and I'm able take the data for the user and put it into an e-mail they can send to whoever.
I'm using am NSFetchedResultsController to populate data onto a UITableView.
It's a simple chat app and I want to load the latest 25 messages onto the table first and load more as the user scrolls up to see older messages (the chat message are in a ascending order).
I call a method that will setFetchLimit: for the NSFetchedResultsController in the willDisplayCell: like so....
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.row == 0)
{
[self performSelector:#selector(getMoreMessages) withObject:nil afterDelay:1.0];
}
}
when the first row of the UITableView has been displayed, getMoreMessages will try to reset the fetchLimit reload the UITableView like so.....
- (void)getMoreMessages
{
maxListItems += 25;
NSLog(#"set maxListItems: %d", maxListItems);
[self.resultsController.fetchRequest setFetchLimit:maxListItems];
[self._tableView reloadData];
}
However, it doesn't seem to be working, the table data will not change.
The initial NSFetchRequest is set like so...
NSFetchRequest *chatDataRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ChatData" inManagedObjectContext:appDelegate.managedObjectContext];
[chatDataRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(key != 0 OR messageNo != 0) and matchNo = %d", matchNo];
[chatDataRequest setPredicate:predicate];
NSSortDescriptor *sortDescripter1 = [[NSSortDescriptor alloc] initWithKey:#"status" ascending:YES];
NSSortDescriptor *sortDescripter2 = [[NSSortDescriptor alloc] initWithKey:#"messageNo" ascending:YES];
NSArray *sortDescripters = [[NSArray alloc] initWithObjects:sortDescripter1, sortDescripter2, nil];
[chatDataRequest setSortDescriptors:sortDescripters];
[sortDescripters release];
[sortDescripter1 release];
[sortDescripter2 release];
[chatDataRequest setFetchLimit:25];
NSFetchedResultsController *fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:chatDataRequest managedObjectContext:appDelegate.managedObjectContext sectionNameKeyPath:nil cacheName:[NSString stringWithFormat:#"%d_chat.cache", matchNumber]];
[chatDataRequest release];
fetchedResultsController.delegate = self;
NSError *error;
BOOL success = [fetchedResultsController performFetch:&error];
if(!success) NSLog(#"error: %#", error);
self.resultsController = fetchedResultsController;
And back to the question.
How can one dynamically change the fetchLimit for an NSFetchedResultsController?
Any hits would be awesome!
Thanks!
Instand using setFetchLimit, using setBatchSize, see below for detail answer.
The count of the fetchedObjects array might not what you want to do, since it does not update the changes from the persistent store. From NSFetchedResultsController documentation:
The results array only includes instances of the entity specified by the fetch request (fetchRequest) and that match its predicate. (If the fetch request has no predicate, then the results array includes all instances of the entity specified by the fetch request.)
The results array reflects the in-memory state of managed objects in the controller’s managed object context, not their state in the persistent store. The returned array does not, however, update as managed objects are inserted, modified, or deleted.
If you only want to fetch 20 objects, set the fetch limit of the NSFetchRequest. If you want only to keep 20 objects in memory, use setBatchSize of the NSFetchRequest object.
figured this one out.
looks like I have to run performFetch: after I change the fetchLimit. :D
[self.resultsController.fetchRequest setFetchLimit:maxListItems];
[self.resultsController performFetch:&error];
Here is an image of the relationship I am debating:
I have a UIScrollView setup as a horizontal scroller that scrolls between 3 different UIViewControllers (containing a UITableView of course and the required delegate methods, etc.)
Each ViewController in the UIScrollView loads a UITableView of a specific MyObjectType.
(E.g. ViewController1 loads a tableview of all MyObjects where its type == MyObjectType.name)
Does this make sense? You'll notice I've setup an inverse relationship between the objects. A MyObjectType can have many MyObject's but a MyObject can only have a single MyObjectType associated to it.
When I first load one of the UIScrollView viewController's I need to determine what MyObjectType this UITableView is for. I have this working fine and I set the Table Header accordingly.
E.g. [type valueForKey:#"name"] where type is a fetched result NSManagedObject of MyObjectType.
The thing is I'm wondering, when I obtain this NSManagedObject of MyObjectType do I not also have access to a NSSet *array (ie. [type valueForKey:#"objects"]) which I can use as the UITableView's datasource? Would this work if after I add or delete an object I save the managedContext and then I always [tableView reloadData] ?
I'm guessing this would work, as long as I don't require the UITableView content to change and update dynamically as new MyObject of this type are added? For this we require a NSFetchedResultsController right?
Here is my code for loading ALL MyObject's into a UITableView (which works):
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"MyObject" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:#"creationDate" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext sectionNameKeyPath:#"transientSectionDate"
cacheName:#"Root"];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
[sort release];
[fetchRequest release];
[theFetchedResultsController release];
return _fetchedResultsController;
}
Could someone PLEASE be as so kind to show my what actual NSPredicate declaration I need to correctly load ONLY MyObject's whose MyObjectType.name == #"XXXXXX"? Let's assume I already have a MyObjectType.name stored in a retained NSString inside the ViewController.
Thanks in advance!
The predicate format string would be:
#"ALL type.name=%#", typeName
However, since you do have a particular MyObjectType object, you already have direct access to the needed MyObject objects and don't have to waste time trying to fetch them. Just convert the set into a sorted array.
To keep apprised of ongoing changes while the table is active, implement observeValueForKeyPath:ofObject:change:context: in the tableview datasource object. Then send addObserver:forKeyPath:options:context: to that particular MyObjectType object like so:
[anObjectType addObserver:self
forKeyPath:#"objects"
options:(NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld)
context:nil];
Now, whenever the objects value of that paticular MyObjectType changes, the tableview's datasource will be notified and can change the table.
See Key-Value Observing Programming Guide for details.