Is there anyway to populate a UITableView with an array but persist checkmarks? - core-data

I have an app that has a tab bar, each tab contains a separate table. The first table uses core data to persist its entries and checkmarks. The second table on the second tab uses an NSMutableArray to populate it (I would use core data but I would have to pre populate it and this table does not allow that) I would like to persist check marks the same way I do in the first table with core data but something is wrong. The code looks like this:
-(void)viewDidLoad {
[super viewDidLoad];
airport = [[NSMutableArray alloc] init];
[airport addObject:#"Passport"];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [airport count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Set up the cell...
NSString *cellValue = [airport objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
//[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
NSManagedObject *item = [[self fetchedResultsController] objectAtIndexPath:indexPath];
cell.textLabel.text = [item valueForKey:#"name"]; //CHANGED TO DETAIL
if ([[item valueForKey:#"check"] boolValue]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSManagedObject *selectedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];
if ([[selectedObject valueForKey:#"check"] boolValue]) {
[selectedObject setValue:[NSNumber numberWithBool:NO] forKey:#"check"];
} else {
[selectedObject setValue:[NSNumber numberWithBool:YES] forKey:#"check"];
}
UITableViewCell *thisCell = [tableView cellForRowAtIndexPath:indexPath];
if (thisCell.accessoryType == UITableViewCellAccessoryNone) {
thisCell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
thisCell.accessoryType = UITableViewCellAccessoryNone;
}
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
I believe the line cell.textLabel.text = [item valueForKey#"name"];
is whats causing it. All that I would like for this to do is have the table populated from the array and check marks persisted. Any help is GREATLY appreciated. Thanks in advance.

This line is replacing the cell text:
cell.textLabel.text = [item valueForKey:#"name"];
that was initially assigned by these lines:
NSString *cellValue = [airport objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
Why are your using the "name" property of "item"?

Related

CoreDataTableViewController does not reload on return from segue

I have several CoreDataTableViewControllers that utilize the helper class from Paul Hegarty's course. Everyone of them works except this one, and I cannot see a difference.
When the table first comes up, it is correctly populated and the segue executes properly when a cell is selected. However when I hit the back button, the table displays (null), (null) everywhere.
I have tried every variant of calling [self useDocument] that I can think of, still to no avail. Any thoughts? Thanks in advance.
//
// TeamTableViewController.m
//
#import "TeamTableViewController.h"
#import "iTrackAppDelegate.h"
#import "CoreDataTableViewController.h"
#import "SchoolRecords.h"
#import "ScheduleViewController.h"
#interface TeamTableViewController ()
#property NSInteger toggle;
#end
#implementation TeamTableViewController
#synthesize iTrackContext = _iTrackContext;
#synthesize schoolSelected = _schoolSelected;
-(void) setSchoolSelected:(SchoolRecords *)schoolSelected
{
_schoolSelected = schoolSelected;
}
-(void) setITrackContext:(NSManagedObjectContext *)iTrackContext
{
if(_iTrackContext != iTrackContext){
if (!iTrackContext) {
MyCoreDataHandler* cdh =
[(iTrackAppDelegate *) [[UIApplication sharedApplication] delegate] cdh];
_iTrackContext = cdh.context;
} else {
_iTrackContext = iTrackContext;
}
}
[self useDocument];
}
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)setupFetchedResultsController // attaches an NSFetchRequest to this UITableViewController
{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"SchoolRecords"];
// no predicate because we want ALL the Athletes
request.sortDescriptors = [NSArray arrayWithObjects:
[NSSortDescriptor sortDescriptorWithKey:#"schoolName" ascending:YES],
nil];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.iTrackContext
sectionNameKeyPath:nil
cacheName:nil];
__block NSInteger myCount;
int64_t delayInSeconds = 5.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self.iTrackContext performBlock:^(void){NSError* requestError = nil;
myCount = [self.iTrackContext countForFetchRequest:request error:&requestError];
NSLog(#"In %# and count of iTrackContext = %lu", NSStringFromClass([self class]),(unsigned long)myCount);
}];
if (!myCount || myCount == 0) {
[self displayAlertBoxWithTitle:#"No Teams" message:#"Have you added athletes yet? \nPlease go to Add Athletes" cancelButton:#"Okay"];
}
});
}
- (void)useDocument
{
if (self.iTrackContext) {
[self setupFetchedResultsController];
} else {
NSString* errorText = #"A problem arose opening the search results database of Athletes.";
[self displayAlertBoxWithTitle:#"File Error" message:errorText cancelButton:#"Okay"];
}
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (!self.iTrackContext) {
MyCoreDataHandler* cdh =
[(iTrackAppDelegate *) [[UIApplication sharedApplication] delegate] cdh];
[self setITrackContext:cdh.context];
} else {
NSLog(#"In %# of %#. Getting ready to call useDocument",NSStringFromSelector(_cmd), self.class);
[self useDocument];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// If divide into sections use line below otherwise return 1.
// return [[self.fetchedResultsController sections] count];
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Do not really need this with only one section, but makes code usable if add sections later.
return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"teamInformation";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
SchoolRecords *schoolResults = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSString* titleText = schoolResults.schoolName;
cell.textLabel.text = titleText;
cell.detailTextLabel.text = [NSMutableString stringWithFormat:#"%#, %#", schoolResults.schoolCity, schoolResults.schoolState];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
}
# pragma navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
[self setSchoolSelected:[self.fetchedResultsController objectAtIndexPath:indexPath]];
// be somewhat generic here (slightly advanced usage)
// we'll segue to ANY view controller that has a photographer #property
if ([segue.identifier isEqualToString:#"scheduleDetailSegue"]) {
// use performSelector:withObject: to send without compiler checking
// (which is acceptable here because we used introspection to be sure this is okay)
NSLog(#"Preparing to passing school with schoolID = %#", self.schoolSelected.schoolID);
[segue.destinationViewController convenienceMethodForSettingSchool:self.schoolSelected];
}
}
- (void) displayAlertBoxWithTitle:(NSString*)title message:(NSString*) myMessage cancelButton:(NSString*) cancelText
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title
message:myMessage
delegate:nil
cancelButtonTitle:cancelText
otherButtonTitles:nil];
[alert show];
}
#end
Well, I am not certain what the problem was. I ended up deleting the "offending" TableViewControllers in StoryBoard and recreated them. That did the trick. In retrospect, I wonder if I did not specify the wrong type of segue from my tabViewController. But I deleted it before I thought of that possibility.

How to get result from Core Data search/filter?

I need some help searching / filtering a Core data entity. The results array returns Null..
I have a root view with a search bar, controller and tableview. This view shows normally.
I'm calling the UISearchBarDelegate and the UISearchDisplayDelegate.
I have a mutable array (searchResults).
My search code is as follows:
-(void) filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSLog(#"%s", __FUNCTION__);
[self.searchResults removeAllObjects];
for (Entity *ent in [self.fetchedResultsController fetchedObjects])
{
if ([scope isEqualToString:#"All"] || [ent.title isEqualToString:scope])
{
NSRange range = [ent.title rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (range.location != NSNotFound)
{
NSLog(#"Adding title '%#' to searchResults as it contains '%#'", ent.title, searchText);
[self.searchResults addObject:ent];
}
}
}
NSLog(#"The searchResults array contains '%#'", searchResults); <<<< RETURNS NULL
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString scope:#"All"];
return YES;
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption
{
[self filterContentForSearchText:[self.searchDisplayController.searchBar text ]scope:#"All"];
return YES;
}
and the cell config code is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"MyVeryOwnCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
Entity *entity = nil;
if (tableView == self.searchDisplayController.searchResultsTableView)
{
//NSLog(#"Configuring cell to show search results");
entity = [self.searchResults objectAtIndex:indexPath.row];
}
else
{
//NSLog(#"Configuring cell to show normal data");
entity = [self.fetchedResultsController objectAtIndexPath:indexPath];
}
cell.textLabel.text = entity.title;
return cell;
}
I must be doing something dumb as the searchResults array appears to be null. I would appreciate any advice.
You just forgot to alloc/init the searchResults array.
;-)

Selecting TableView cell returns incorrect object

I followed a tutorial I found online to create a tableview with sections and an index from an array of custom objects. This code works with the exception that when I select a row in the table I the index path for that section and not for the entire array. I can see why it doesn't work but I can't figure out how to address the fix, this is my cell for tableview code.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"NameCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
// Configure the cell...
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
int displayOrder = [defaults integerForKey:#"displayOrder"];
int sortOrder = [defaults integerForKey:#"sortOrder"];
NSString *alphabet = [listIndex objectAtIndex:[indexPath section]];
NSPredicate *sectionPredicate = [[NSPredicate alloc] init];
if (sortOrder == 1) {
//NSLog(#"fName is predicate at cell level");
sectionPredicate = [NSPredicate predicateWithFormat:#"fName beginswith[c] %#", alphabet];
} else {
//NSLog(#"lName is predicate at cell level");
sectionPredicate = [NSPredicate predicateWithFormat:#"lName beginswith[c] %#", alphabet];
}
NSArray *sectionContacts = [filteredList filteredArrayUsingPredicate:sectionPredicate];
if (isSearching) {
current = [filteredList objectAtIndex:indexPath.row];
} else{
current = [sectionContacts objectAtIndex:indexPath.row];
}
if (displayOrder == 1) {
NSString *fullName = [NSString stringWithFormat:#"%# %#",[current valueForKey:#"fName"],[current valueForKey:#"lName"]];
[cell.textLabel setText:fullName];
//NSLog(#"FirstNameFirst");
} else {
NSString *fullName = [NSString stringWithFormat:#"%# %#",[current valueForKey:#"lName"],[current valueForKey:#"fName"]];
[cell.textLabel setText:fullName];
//NSLog(#"LastNameFirst");
}
[cell.detailTextLabel setText:[current valueForKey:#"extension"]];
return cell; }
THen I call the segue with this code.
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"showContact"]) {
DetailViewController *dvc = [segue destinationViewController];
NSIndexPath *path = [self.tableView indexPathForSelectedRow];
NSDictionary *c = [filteredList objectAtIndex:path.row];
[dvc setCurrentContact:c];
[searchBar resignFirstResponder];
} }
The problem is that the objectAtIndex:path.row returns the index for that section but it isn't modified for the entire array, so if a name in the "B" section that is at index 4 of that section is tapped it returns the object at index 4 of the primary array. I have been scratching my head to figure out how to get the index for the full array and not for the one that is only local to that section.
I'll buy you a 6 pack of your favorite beverage if you can help!
Thanks!
You do it the same way that they do it in the first function, so change your prepareForSegue to this:
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showContact"]) {
DetailViewController *dvc = [segue destinationViewController];
NSIndexPath *path = [self.tableView indexPathForSelectedRow];
NSDictionary *c;
NSString *alphabet = [listIndex objectAtIndex:[path section]];
NSPredicate *sectionPredicate = [[NSPredicate alloc] init];
if (sortOrder == 1) {
sectionPredicate = [NSPredicate predicateWithFormat:#"fName beginswith[c] %#", alphabet];
} else {
sectionPredicate = [NSPredicate predicateWithFormat:#"lName beginswith[c] %#", alphabet];
}
NSArray *sectionContacts = [filteredList filteredArrayUsingPredicate:sectionPredicate];
if (isSearching) {
c = [filteredList objectAtIndex:path.row];
} else{
c = [sectionContacts objectAtIndex:path.row];
}
[dvc setCurrentContact:c];
[searchBar resignFirstResponder];
}
}
Note that it would probably be best to pull the common code out and make a separate function instead of using it twice like this.

click on searchbar terminates my program on Iphones using Objective C

I want to add simple search bar in the table view. Program getting run with no error, but when I try to add text in search bar, program gets terminates
following is my code:
#interface RootViewController : UITableViewController<UISearchBarDelegate> {
NSArray *list;
IBOutlet UISearchBar *searchBar;
NSMutableArray *searchresult;
BOOL isSearchon;
BOOL canSelectRow;
}
#property(nonatomic, retain)NSArray *list;
#property(nonatomic, retain)IBOutlet UISearchBar *searchBar;
-(void)doneSearching:(id)sender;
-(void)searchlist;
#end
//in .m
#implementation RootViewController
#synthesize list,searchBar;
-(void)doneSearching:(id)sender
{
isSearchon=NO;
canSelectRow=YES;
self.tableView.scrollEnabled=YES;
self.navigationItem.rightBarButtonItem=nil;
[searchBar resignFirstResponder];
[self.tableView reloadData];
}
-(void)searchlist
{NSString *searchText = searchBar.text;
[searchresult removeAllObjects];
for(NSString *str in list)
{
NSRange titleResultsRange=[str rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (titleResultsRange.length > 0)
[searchresult addObject:str];
}
}
- (void) searchBarSearchButtonClicked:(UISearchBar *)theSearchBar {
[self searchlist];
}
- (void)searchBar:(UISearchBar *)theSearchBar textDidChange:(NSString *)searchText {
if([searchText length]>0)
{isSearchon =YES;
canSelectRow=YES;
self.tableView.scrollEnabled=YES;
[self searchlist];}
else{
isSearchon =NO;
canSelectRow=NO;
self.tableView.scrollEnabled=NO;
}
[self.tableView reloadData];
}
- (void)viewDidLoad {
[super viewDidLoad];
list = [[NSArray alloc]initWithObjects:#"rohan",#"vidhya",#"kavita",#"pushkar",nil];
self.tableView.tableHeaderView = searchBar;
searchBar.autocorrectionType=UITextAutocorrectionTypeYes;
searchresult=[[NSMutableArray alloc]init];
isSearchon =NO;
canSelectRow=YES;
self.navigationItem.title = #"Names";
}
-(void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
isSearchon =YES;
canSelectRow=NO;
self.tableView.scrollEnabled=NO;
self.navigationItem.rightBarButtonItem=[[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(doneSearching:)]autorelease];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if(isSearchon){
return [searchresult count];}
else{
return [list count];}
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
if(isSearchon)
{
cell.text=[searchresult objectAtIndex:indexPath.row];}
else{
cell.text=[list objectAtIndex:indexPath.row];}
// Configure the cell.
return cell;
}
- (void)dealloc {
[super dealloc];
[searchBar release];
}
#end
I have edited my code in my question...it works fine nw....AnyOne willing to use searchbar in tableview in Iphones can refer the code.

How can I insert new objects at top of UITableView backed by Core Data/NSFetchedResultsController?

I have a tableview that is successfully incorporating an NSFetchedResultsController. However, I need the topmost cell in my tableview to read, "Add new object" and have UITableViewCellEditingStyleInsert instead of the default UITableViewCellEditingStyleDelete.
The FetchResultsController wants to check the managedObjectContext for objects--both to determine number of rows and to populate the table cells. The only way I can think to get around this is to create a dummy object, but I feel like there ought to be a more elegant solution.
UPDATE:
For those who might be curious as to what solution I ended up with, I decided to have my insert cell at the bottom, not the top. Here is the relevant code:
- (void)viewDidLoad {
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
//self.clearsSelectionOnViewWillAppear = NO;
self.editing = YES;
self.tableView.allowsSelectionDuringEditing = YES;
self.tableView.delegate = self;
RubricAppDelegate *appDelegate = (RubricAppDelegate *)[[UIApplication sharedApplication] delegate];
managedObjectContext = [appDelegate managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"myClass" inManagedObjectContext:managedObjectContext];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"classID" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil cacheName:nil];
NSError *error;
[fetchedResultsController performFetch:&error];
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
NSLog(#"Number of sections = %d", [[fetchedResultsController sections] count]);
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
id <NSFetchedResultsSectionInfo> myClass = [[fetchedResultsController sections] objectAtIndex:section];
NSLog(#"Number of classes = %d", [myClass numberOfObjects]);
return ([[fetchedResultsController fetchedObjects] count] + 1);
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSLog(#"FRC count + 1 = %d", ([[fetchedResultsController fetchedObjects] count] + 1));
if (indexPath.row == ([[fetchedResultsController fetchedObjects] count])) {
cell.textLabel.text = #"Add New Class";
}
else {
myClass *theClass = [fetchedResultsController objectAtIndexPath:indexPath];
NSLog(#"Class name is: %#", theClass.classTitle);
cell.textLabel.text = theClass.classTitle;
}
return cell;
}
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == [[fetchedResultsController fetchedObjects] count]) {
return UITableViewCellEditingStyleInsert;
}
return UITableViewCellEditingStyleDelete;
}
The result (with some junk data):
Now my only issue is getting the delete functions to work properly. You can follow my post on that issue here
Normally the add row is at the bottom.
You can accomplish this by changing the -tableView:numberOfRowsInSection: and the -tableView:cellForRowAtIndexPath: methods to adjust the cell count and adjust for it. So your -tableView:numberOfRowsInSection: would return N+1 and your -tableView:cellForRowAtIndexPath: would get object at N-1 unless N == 0 then it would return your "Add new object" cell.
There is no need to mess with the underlying Core Data elements as this is strictly a UI issue.
Update
But now I'm not sure how to return the count of my fetched objects (assuming that is what I used for "N" in your above answer). Also, wouldn't I want -tableView:cellForRowAtIndexPath to return my "Add new object" cell when the indexPath.row = (N + 1), not N = 0? I may be misunderstanding what "N" equates to, but I thought it just meant count of fetched objects.
Yes it is the count of actual objects.
You do want your -tableView:cellForRowAtIndexPath: to return a cell for your "Add new object" otherwise what is the point? You just want it to return a different type of cell.
All you are doing in this solution is adding a cell that is not part of the NSFetchedResultsController and then compensating for it when you are retrieving an actual object from the NSFetchedResultsController and when the user selects a cell.

Resources