NSUserDefaults unable to save and read my custom objects - objective-c

I have a class Notification that implements the NSCoding protocol.
I have an array of notifications.I am trying to save the notifications with NSUserDefaults.In my app delegate notifications is a NSMutableArray that contains the Notification objects.That's my app delegate:
+ (void) initialize
{
NSUserDefaults* defaults=[NSUserDefaults standardUserDefaults];
[defaults registerDefaults: [NSDictionary dictionaryWithObject: [NSKeyedArchiver archivedDataWithRootObject: [NSArray array]] forKey: #"notificationsData"]];
}
- (id) init
{
self=[super init];
if(self)
{
NSUserDefaults* defaults=[NSUserDefaults standardUserDefaults];
NSData* notificationsData=[defaults objectForKey: #"notificationsData"];
notifications= [[NSKeyedUnarchiver unarchiveObjectWithData: notificationsData]mutableCopy];
}
return self;
}
- (void) applicationWillTerminate:(NSNotification *)notification
{
NSUserDefaults* defaults=[NSUserDefaults standardUserDefaults];
NSData* notificationsData=[NSKeyedArchiver archivedDataWithRootObject: notifications];
[defaults setObject: notificationsData forKey: #"notificationsData"];
}
In the Notification class text and title are of type NSString (both readwrite), and date is of type NSDate (also this has readwrite property).This is how I implement the NSCoding protocol:
- (void) encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject: date forKey: #"date"];
[aCoder encodeObject: title forKey: #"title"];
[aCoder encodeObject: text forKey: #"text"];
}
- (id) initWithCoder:(NSCoder *)aDecoder
{
self=[super init];
if(self)
{
date=[aDecoder decodeObjectForKey: #"data"];
title=[aDecoder decodeObjectForKey: #"title"];
text=[aDecoder decodeObjectForKey: #"text"];
}
return self;
}
So I have these problems:
When the application terminates I get EXC_BAD_ACCESS in the
Notification class, when I try to encode text with NSKeyedArchiver;
The notifications aren't saved and the array is always long zero
when the application starts.
Update: With more debug I discovered where the application crashes.There is more code to see (I'm using a table view to display the data):
- (NSInteger) numberOfRowsInTableView:(NSTableView *)tableView
{
return [notifications count];
}
- (id) tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
// Debug
id obj=[[notifications objectAtIndex: row] valueForKey: [tableColumn identifier]];
Class class=[obj class];
// What you see above is just for debug purposes
return [[notifications objectAtIndex: row] valueForKey: [tableColumn identifier]];
}
- (void) tableViewSelectionDidChange:(NSNotification *)notification
{
NSInteger row=[tableView selectedRow];
if(row >= 0 && row< [notifications count])
[removeButton setEnabled: YES];
else
[removeButton setEnabled: NO];
}
The last method called is this:
- (id) tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row;
Probably the problem is that the data is somehow corrupted and the value returned by this method is not valid.Anyway the app doesn't crash in this method, but after this method.
If I load two objects from user defaults, only one object gets loaded before crashing (so the method gets called once).
However I'm still unable to get the real reason of the crash.
More code:
- (IBAction) addNotification :(id)sender
{
Notification* notification=[[Notification alloc]init];
[notification setDate: [datePicker dateValue]];
[notification setText: [textView string]];
[notifications addObject: notification];
[tableView reloadData];
}
- (IBAction)removeNotification:(id)sender
{
[notifications removeObjectAtIndex: [tableView selectedRow]];
[tableView reloadData];
}
addNotification and removeNotification are both triggered by buttons.
EDIT: I discovered that I wasn't using ARC, but even if I turn it on the app crashes.

In the line:
date=[aDecoder decodeObjectForKey: #"data"];
#"data" doesn't match the encoder key. You really want:
date=[aDecoder decodeObjectForKey: #"date"];

You might need to call [NSUserDefaults synchronize] since it will not happen automatically when the application suddenly exits:
- (void) applicationWillTerminate:(NSNotification *)notification
{
NSUserDefaults* defaults=[NSUserDefaults standardUserDefaults];
NSData* notificationsData=[NSKeyedArchiver archivedDataWithRootObject: notifications];
[defaults setObject: notificationsData forKey: #"notificationsData"];
[defaults synchronize];
}

You couldn't guess what was wrong: my bad, I forgot to enable ARC and some objects were released when they shouldn't.

Related

Loading data once the data is in array

[array addObject:textdata.text];
NSUserDefaults *save = [NSUserDefaults standardUserDefaults];
[save setObject:array forKey:#"success" ];
[save synchronize];
-(void) viewDidLoad
NSUserDefaults *viewdata1 = [NSUserDefaults standardUserDefaults];
[viewdata1 objectForKey:#"success"];
[viewdata1 synchronize];
[tabledata reloadData];
Once the data is saved in the array, how do I upload it once the app runs again? I want the data to load back in the table once.
The first step is to retrieve it from user defaults. The second step is not to drop it on the floor.
[viewdata1 objectForKey:#"success"];
This does one, but not the other: You retrieve it, but then you drop it on the floor.
You need to store the object as the value of a property (which means you will need to declare a property for that purpose), then, in your table view's data source, return the count of that array as your number of rows and objects in the array (or properties of those objects) as the row values.
Also, you shouldn't need to call synchronize, especially after retrieving the value.
You should make like this:
TSTableViewController.h:
#property(nonatomic, readwrite, retain) NSMutableArray* dataSource;
TSTableViewController.m:
- (id) init
{
if ((self = [super init]))
{
[[NSNotificationCenter defaultCenter] addObserver: self selector: #selector(applicationDidEnterBackground:)
name: UIApplicationDidEnterBackgroundNotification
object: nil];
}
return self;
}
- (void) applicationDidEnterBackground: (NSNotification*) notification
{
[[NSUserDefaults standardUserDefaults] setObject: self.dataSource
forKey: #"success" ];
}
- (void) viewDidLoad
{
[super viewDidLoad];
NSArray* array = [[NSUserDefaults standardUserDefaults] objectForKey: #"success"];
if (array)
{
self.dataSource = [NSMutableArray arrayWithArray: array];
}
else
{
self.dataSource = [[[NSMutableArray alloc] init] autorelease];
}
[tableView reloadData];
}
- (void) addDataToDataSource
{
[self.dataSource addObject: textdata.text];
[tabledata reloadData];
}
- (void) dealloc
{
[dataSource release];
dataSource = nil;
[super dealloc];
}

EXC_BAD_ACCESS when trying to deserialize a subclass object from AFHTTPClient

I have a subclass of AFHTTPClient with NSCoding protocol methods implemented:
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (!self)
return nil;
self.isLoggedIn = [aDecoder decodeBoolForKey:#"isLoggedIn"];
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[super encodeWithCoder:aCoder];
[aCoder encodeBool:self.isLoggedIn forKey:#"isLoggedIn"];
}
I also implemented the method for setting default header for the token & there I archive the client:
- (void)setAuthorizationHeaderWithToken:(NSString *)token {
[self setDefaultHeader:#"Authorization" value:[NSString stringWithFormat:#"OAuth %#", token]];
[self setIsLoggedIn:YES];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:kGCClient];
[[NSUserDefaults standardUserDefaults] synchronize];
}
And I deserialize the client in the - (id)initWithBaseURL:(NSURL *)url implementation in my subclass:
- (id)initWithBaseURL:(NSURL *)url {
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:kGCClient];
GCClient *client = [NSKeyedUnarchiver unarchiveObjectWithData:data];
if (client)
return self = client;
self = [super initWithBaseURL:url];
if (!self) {
return nil;
}
[self setIsLoggedIn:NO];
return self;
}
The issue is that once I'm logged in, the next time I open the app it crushes on the deserialization of the client, in the AFHTTPClient class - (id)initWithCoder:(NSCoder *)aDecoder method, on the first try to decode a object & in debugger it says that the value returned is not an Objective-C object.
NSURL *baseURL = [aDecoder decodeObjectForKey:#"baseURL"];
Two things:
Use the Keychain, rather than NSUserDefaults, to store credentials.
Instead of messing with NSCoding, simply override -initWithBaseURL, (making sure to call super, of course), and set the Authorization header based on the value stored in the keychain (if it exists). The isLoggedIn property could (and should) be defined as a derived readonly property, which returns YES when the Authorization header is present, and NO otherwise.

NSFetchedResultsController doesn't fetch results even though items are created in the database

I'm new to iOS, so apologies if this is brain dead simple... I've been iteratively working through some small proof of concept apps before starting to implement my full app so that it wouldn't be as overwhelming. I had my table view working fine when I created it following the "Your Second iOS App" tutorial on the Apple website. Now I've tried creating it in a tab bar app though, and I'm seeing problems with the NSFetchedResultsController, and I'm not sure if it's related to something that I'm doing wrong in the Storyboard, or something else.
I have a Tab Bar Controller that connects to a Table View Controller (CatalogViewController.h/m) that is embedded in a Navigation Controller. The Table View Controller is configured to have static cells. In the first static cell I have a push segue to another Table View Controller (FoodCatalogViewController.h/m) which is configured to use dynamic prototypes - this is the view in which I expect to see the objects from my database (from the Food entity - currently just shows name and calories). This view has an "Add" button to create new entries in the database - the add button has a modal segue to another static table view (AddFoodViewController.h/m) that is embedded in it's own navigation controller. I know that the "Add" button is working and that it's view is correctly connecting to the database (i.e. I'm passing/setting the NSManagedObjectContext correctly), because if I open the app's sqlite database file using "SQLite Database Browser", I see the items that I've added in the simulator. I just don't understand why they're not getting displayed in my table view via the NSFetchedResultsController. I stepped through the code using breakpoints and confirmed that the performFetch code is being called in my FoodCatalogViewController's fetchedResultsController function. I added a debug NSLog line in the numberOfRowsInSection code, and it seems to be nil, so I never get into cellForRowAtIndexPath or configureCell. So it looks like the NSFetchedResultsController is the culprit - I just don't know why it's not fetching the results correctly, and what I can do to debug this further. Can anyone help me with this?
In order to pass the Core Data info through the hierarchy, I have the following code snippets:
AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
// Setup the Catalogs Tab
UINavigationController *navigationController = [[tabBarController viewControllers] objectAtIndex:0];
CatalogViewController *catalogViewController = [[navigationController viewControllers] objectAtIndex:0];
catalogViewController.managedObjectContext = self.managedObjectContext;
return YES;
}
CatalogViewController.m (the first table view controller in the sequence - I pass the NSManagedObjectContext through to it):
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"BrowseFoodCatalog"]) {
[[segue destinationViewController] setManagedObjectContext:self.managedObjectContext];
}
}
FoodCatalogViewController.h (the second table view controller in the sequence - I use the NSManagedObjectContext to setup the NSFetchedResultsController):
#interface FoodCatalogViewController : UITableViewController <NSFetchedResultsControllerDelegate>
#property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;
#property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
- (void) addFoodWithName:(NSString *)name calories:(NSNumber *)calories;
#end
FoodCatalogViewController.m (the second table view controller in the sequence - I use the NSManagedObjectContext to setup the NSFetchedResultsController):
#interface FoodCatalogViewController () <AddFoodViewControllerDelegate>
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;
#end
#implementation FoodCatalogViewController
#synthesize fetchedResultsController = __fetchedResultsController;
#synthesize managedObjectContext = __managedObjectContext;
- (NSFetchedResultsController *)fetchedResultsController
{
if (__fetchedResultsController != nil) {
return __fetchedResultsController;
}
// Set up the fetched results controller.
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Food" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// 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:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Master"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return __fetchedResultsController;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"FoodCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [[managedObject valueForKey:#"name"] description];
NSNumber *calorieNum = [managedObject valueForKey:#"calories"];
cell.detailTextLabel.text = [[calorieNum stringValue] description];
}
Additional Info
Not sure if this is relevant, but in order to get CoreData to automatically be included in my project, I started with the Single View template, but modified it's TemplateInfo.plist to add the following line under the similar line for storyboarding:
<string>com.apple.dt.unit.coreDataCocoaTouchApplication</string>
I'd found this online somewhere in someone's forum or something. Could this have messed up the CoreData somehow?
Additional Code
As requested, here's the code that I use to add new elements to the database:
AddFoodViewController.h:
#import <UIKit/UIKit.h>
#protocol AddFoodViewControllerDelegate;
#interface AddFoodViewController : UITableViewController <UITextFieldDelegate>
#property (weak, nonatomic) IBOutlet UITextField *foodNameInput;
#property (weak, nonatomic) IBOutlet UITextField *caloriesInput;
#property (weak, nonatomic) id <AddFoodViewControllerDelegate> delegate;
- (IBAction)save:(id)sender;
- (IBAction)cancel:(id)sender;
#end
#protocol AddFoodViewControllerDelegate <NSObject>
- (void)addFoodViewControllerDidCancel:(AddFoodViewController *)controller;
- (void)addFoodViewControllerDidSave:(AddFoodViewController *)controller name:(NSString *)name calories:(NSNumber *)calories;
#end
AddFoodViewController.m:
#import "AddFoodViewController.h"
#implementation AddFoodViewController
#synthesize foodNameInput;
#synthesize caloriesInput;
#synthesize delegate = _delegate;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)viewDidUnload
{
[self setFoodNameInput:nil];
[self setCaloriesInput:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if ((textField == self.foodNameInput) || (textField == self.caloriesInput )) {
[textField resignFirstResponder];
}
return YES;
}
- (IBAction)save:(id)sender {
int caloriesInt = [self.caloriesInput.text intValue];
NSNumber *caloriesNum = [NSNumber numberWithInt:caloriesInt];
[[self delegate] addFoodViewControllerDidSave:self name:self.foodNameInput.text calories:caloriesNum];
}
- (IBAction)cancel:(id)sender {
[[self delegate] addFoodViewControllerDidCancel:self];
}
#end
FoodCatalogViewController.m (the AddFoodViewControllerDelegate protocol code to add to the database):
- (void)addFoodViewControllerDidCancel:(AddFoodViewController *)controller {
[self dismissViewControllerAnimated:YES completion:NULL];
}
- (void)addFoodViewControllerDidSave:(AddFoodViewController *)controller name:(NSString *)name calories:(NSNumber *)calories {
if ([name length]) {
[self addFoodWithName:name calories:calories];
[[self tableView] reloadData];
}
[self dismissModalViewControllerAnimated:YES];
}
- (void) addFoodWithName:(NSString *)name calories:(NSNumber *)calories {
// Create a new instance of the entity managed by the fetched results controller.
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
NSLog(#"entity name is %#", [entity name]);
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
// If appropriate, configure the new managed object.
// Normally you should use accessor methods, but using KVC here avoids the need to add a custom class to the template.
[newManagedObject setValue:name forKey:#"name"];
[newManagedObject setValue:calories forKey:#"calories"];
CFUUIDRef uuidRef = CFUUIDCreate(NULL);
CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef);
NSString* uuid = [NSString stringWithString:(__bridge NSString *)uuidStringRef];
[newManagedObject setValue:uuid forKey:#"uuid"];
// Save the context.
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
More Debug Info
Strange - it looks like the fetchedResultsController isn't working correctly in the FoodCatalogViewController even though the managedObjectContext seems to be working... Here's the modified fetchedResultsController function from FoodCatalogViewController.m with some debug NSLog statements and replacing self.fetchedResultsController with __fetchedResultsController (because I wondered if that was causing the problem).
Here's the output from the fetchedResultsController function NSLog calls:
2012-01-29 10:22:21.118 UltraTrack[19294:fb03] Result: (
"<Food: 0x6e651b0> (entity: Food; id: 0x6e64630 <x-coredata://8A10B827-9F10-4760-934C-0061A982B73C/Food/p1> ; data: <fault>)",
"<Food: 0x6e653e0> (entity: Food; id: 0x6e61870 <x-coredata://8A10B827-9F10-4760-934C-0061A982B73C/Food/p3> ; data: <fault>)",
"<Food: 0x6e65450> (entity: Food; id: 0x6e64420 <x-coredata://8A10B827-9F10-4760-934C-0061A982B73C/Food/p4> ; data: <fault>)",
"<Food: 0x6e654c0> (entity: Food; id: 0x6e64430 <x-coredata://8A10B827-9F10-4760-934C-0061A982B73C/Food/p5> ; data: <fault>)",
"<Food: 0x6e65530> (entity: Food; id: 0x6e64e80 <x-coredata://8A10B827-9F10-4760-934C-0061A982B73C/Food/p2> ; data: <fault>)",
"<Food: 0x6e655b0> (entity: Food; id: 0x6e64e90 <x-coredata://8A10B827-9F10-4760-934C-0061A982B73C/Food/p6> ; data: <fault>)"
)
2012-01-29 10:22:21.907 UltraTrack[19294:fb03] Number or objects: 6
And here's the modified fetchedResultsController function:
- (NSFetchedResultsController *)fetchedResultsController
{
if (__fetchedResultsController != nil) {
return __fetchedResultsController;
}
// Set up the fetched results controller.
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Food" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// 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:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Master"];
aFetchedResultsController.delegate = self;
__fetchedResultsController = aFetchedResultsController;
NSArray *result = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil];
NSLog(#"Result: %#", result);
NSError *error = nil;
if (![__fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
NSLog(#"Number or objects: %d", [__fetchedResultsController.fetchedObjects count]);
return __fetchedResultsController;
}
Someone suggested that the sections were the problem, so I hard-coded numberOfSectionsInTableView to return 1, and then the first object from the fetchResults seems to be handled correctly, but I get the following exception:
2012-01-29 10:29:27.296 UltraTrack[19370:fb03] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'no object at index 1 in section at index 0'
If I hardcode numberOfRowsInSection to also return 1, then the first object from my database is correctly displayed in the table view. What could be the problem with regard to the sections info in the fetchedResultsController? Could I have setup something incorrectly in the storyboard for the table view with regard to sections?
Here's the 2 table view functions where I've tried the hard-coding:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
//NSLog(#"Number of sections in table view is %d", [[self.fetchedResultsController sections] count]);
//return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"Number or objects: %d", [self.fetchedResultsController.fetchedObjects count]);
// If I return 1, the object is displayed correctly, if I return count, I get the exception
//return 1;
return [self.fetchedResultsController.fetchedObjects count];
// Return the number of rows in the section.
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
NSLog(#"Number of rows being returned is %d", [sectionInfo numberOfObjects]);
return [sectionInfo numberOfObjects];
}
From your description it seems that the code with the sections is the culprit. But you are not actually using any sections. So try this to simplify:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.fetchedResultsController.fetchedObjects count];
}
If this still returns zero rows, check your fetchedObjects.count in the fetchedResultsController getter method.

How do I access the data source in my NSTableView to allow editing of a row in the table?

I'm trying to access the array (data source) in my NSTableView to allow replacing the string that is in the row with a new string.The app consist of an NSTextfield for data entry and a button to add the entry so that it's displayed in the NSTableView. What I want is to be able to double click the NSTableView and replace what ever string is there with a new string, but I'm not sure how to do this.Here is my code so far
#implementation AppController
-(id)init
{
[super init];
[tableView setDataSource:self];
[tableView setDelegate:self];
array = [[NSMutableArray alloc ] init];
//NSLog(#"this is my delegate %#",[tableView delegate]);
return self;
}
-(IBAction)addItem:(id)sender
{
inputString = [textField stringValue];
[array addObject:inputString];
[tableView reloadData];
return;
}
- (int)numberOfRowsInTableView:(NSTableView *)aTableView
{
return [array count];
}
- (id) tableView:(NSTableView *)aTableView
objectValueForTableColumn:(NSTableColumn *)aTableColumn
row:(int)rowIndex
{
//NSLog(#"this is the object %#",[array objectAtIndex:rowIndex]);
return [array objectAtIndex:rowIndex];
}
-(IBAction) replaceItem:(id)sender
{
NSString *newString = [[NSString alloc]init];
NSLog(#"The selected row %d",[tableView selectedRow]);
newString = [textField stringValue];
[array addObject:newString];
[array replaceObjectAtIndex:[tableView selectedRow ] withObject: newString];
NSLog(#"this is the new sting %#",newString);
[tableView reloadData];
}
#end
I think you're looking for these datasource and delegate methods:
-tableView:setObjectValue:forTableColumn:row:
-tableView:shouldEditTableColumn:row:

NSCFString objectAtIndex unrecognized selector sent to instance 0x5d52d70

I'm new to iPhone development, and been having a hard time trying to figure out why my table isn't working. It could be something with Core Data, I'm not sure. The viewDidLoad method works fine at the start, but when I try to scroll the table view, when a next row appears, I get the error:
NSInvalidArgumentException', reason: '-[NSCFString objectAtIndex:]: unrecognized selector sent to instance 0x5d52d70'
my View Controller.h:
#import <UIKit/UIKit.h>
#define kTableViewRowHeight 66
#interface RostersViewController : UIViewController
<UITableViewDelegate, UITableViewDataSource> {
NSArray *objects;
}
#property(nonatomic,retain) NSArray *objects;
#end
my View Controller.m:
#import "RostersViewController.h"
#import "CogoAppDelegate.h"
#import "TeamCell.h"
#implementation RostersViewController
#synthesize objects;
- (void)viewDidLoad {
CogoAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Teams" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
NSError *error;
objects = [context executeFetchRequest:request error:&error];
if (objects == nil)
{
NSLog(#"There was an error!");
// Do whatever error handling is appropriate
}
[request release];
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillTerminate:)
name:UIApplicationWillTerminateNotification
object:app];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[objects release];
[super dealloc];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [objects count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"cell"];
id currObj = [objects objectAtIndex:indexPath.row];
cell.textLabel.text = [currObj valueForKey:#"name"];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return kTableViewRowHeight;
}
#end
Thanks for any help. It's very much appreciated.
In viewDidLoad change objects = [context executeFetchRequest:request error:&error]; to self.objects = [context executeFetchRequest:request error:&error];
executeFetchRequest returns an autoreleased object, which you are then storing directly to the ivar, this turns into a garbage pointer at a later point. Which just so happens to end up pointing to a string.
Using self.objects makes it use the synthesized setter and will retain it.