NSFetchedResultsController & UITableViewController not communicating - objective-c

I have some "details" saved into my Core Data, and I'm trying to call it out from an NSFetchedResultsController into my tableView. For whatever reason, the tableView isn't populating once the block is finished running.
I have been searching and asking everywhere I can to try and figure out this whole Core Data debacle. Hopefully, someone on here is kind enough to help me out!
HomeViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.title = #"Home";
self.navigationController.navigationBar.tintColor = [UIColor colorWithRed:0 green:0.7 blue:2.3 alpha:1];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCompose target:self action:#selector(addShindy:)];
self.tableView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"light_alu.png"]];
self.tableView.opaque = NO;
self.tableView.backgroundView = nil;
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:#selector(refresh:)
forControlEvents:UIControlEventValueChanged];
self.refreshControl = refreshControl;
[self setShindyDatabase:self.shindyDatabase];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (!self.shindyDatabase) {
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
self.shindyDatabase = [[UIManagedDocument alloc] initWithFileURL:url];
}
}
- (void)refresh:(UIRefreshControl *)sender
{
[self useDocument];
[sender endRefreshing];
}
- (void)addShindy:(UIBarButtonItem *)sender
{
AddShindyViewController *addShindyViewController = [[AddShindyViewController alloc] initWithNibName:#"AddShindyViewController" bundle:nil];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:addShindyViewController];
[self presentViewController:navController animated:YES completion:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma Core Date Stack
- (void)fetchShindyDataIntoDocument:(UIManagedDocument *)document
{
dispatch_queue_t fetchIntoDocument = dispatch_queue_create("Fetch Into Document", nil);
dispatch_async(fetchIntoDocument, ^{
NSArray *shindys = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
for (NSDictionary *shindyInfo in shindys) {
[Shindy shindyWithShindyDBInfo:shindyInfo inManagedObjectContext:document.managedObjectContext];
}
[document saveToURL:document.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:NULL];
});
}
- (void)setShindyDatabase:(UIManagedDocument *)shindyDatabase
{
if (_shindyDatabase != shindyDatabase) {
_shindyDatabase = shindyDatabase;
[self useDocument];
}
}
- (void)useDocument
{
NSError *error = nil;
if (![[NSFileManager defaultManager] fileExistsAtPath:[self.shindyDatabase.fileURL path]]) {
NSLog(#"Create document");
[self.shindyDatabase saveToURL:self.shindyDatabase.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
[self setupFetchedResultsController];
[self fetchShindyDataIntoDocument:self.shindyDatabase];
if (!success) {
NSLog(#"error for creation of document: %#", [error localizedDescription]);
}
}];
} else if (self.shindyDatabase.documentState == UIDocumentStateClosed) {
NSLog(#"Closed document");
[self.shindyDatabase.managedObjectContext.parentContext performBlock:^{
[self setupFetchedResultsController];
}];
} else if (self.shindyDatabase.documentState == UIDocumentStateNormal) {
NSLog(#"Normal Document");
[self setupFetchedResultsController];
}
if (error) {
NSLog(#"Error in useDocument: %#", [error localizedDescription]);
}
}
- (void)setupFetchedResultsController
{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Shindy"];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"details" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
// request.predicate = [NSPredicate predicateWithFormat:#"details = %#", [self.shindyDatabase valueForKey:#"details"]];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.shindyDatabase.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
}
#pragma mark - Table view data source
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 75;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
Shindy *shindy = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSLog(#"%#", shindy.details);
cell.textLabel.text = shindy.details;
Shindy+CreateDB.m
+ (Shindy *)shindyWithShindyDBInfo:(NSDictionary *)shindyInfo
inManagedObjectContext:(NSManagedObjectContext *)context
{
Shindy *shindy = nil;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Shindy"];
// NSSortDescriptor *dateAndTimeSort = [NSSortDescriptor sortDescriptorWithKey:#"dateAndTime" ascending:YES];
NSSortDescriptor *detailsSort = [NSSortDescriptor sortDescriptorWithKey:#"details" ascending:YES];
// NSSortDescriptor *locationSort = [NSSortDescriptor sortDescriptorWithKey:#"location" ascending:YES];
// NSSortDescriptor *nameSort = [NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES];
// NSSortDescriptor *photoSort = [NSSortDescriptor sortDescriptorWithKey:#"photo" ascending:YES];
// NSSortDescriptor *timePostedSort = [NSSortDescriptor sortDescriptorWithKey:#"timePosted" ascending:YES];
// title
request.sortDescriptors = [NSArray arrayWithObject:detailsSort];
// request.sortDescriptors = [NSArray arrayWithObjects:dateAndTimeSort, detailsSort, locationSort, nameSort, photoSort, timePostedSort, nil];
NSError *error = nil;
NSArray *matches = [context executeFetchRequest:request error:&error];
if (error) {
NSLog(#"document failed in file: %#", [error localizedDescription]);
}
if (!matches || ([matches count] > 1)) {
NSError *error = nil;
NSLog(#"error in DBInfo: %#", [error localizedDescription]);
} else if ([matches count] == 0) {
shindy = [NSEntityDescription insertNewObjectForEntityForName:#"Shindy" inManagedObjectContext:context];
// shindy.dateAndTime = [shindyInfo objectForKey:#"dateAndTime"];
shindy.details = [shindyInfo objectForKey:#"details"];
// shindy.location = [shindyInfo objectForKey:#"location"];
// shindy.name = [shindyInfo objectForKey:#"name"];
// shindy.photo = [shindyInfo objectForKey:#"photo"];
// shindy.timePosted = [shindyInfo objectForKey:#"timePosted"];
// title
// Guestlist? The rest?
// Use below for reference
// shindy.whoseShindy = [User userWithName:[shindyInfo objectForKey:#"whoseShindy"] inManagedObjectContext:context];
} else {
shindy = [matches lastObject];
}
return shindy;
}
EDIT:
I suppose I should have also shown a file of which I have set as a subclass of my HomeViewController. It's basically the same thing as the code that is given to you to paste in the Apple Documentation
CoreDataTableViewController.h
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#interface CoreDataTableViewController : UITableViewController <NSFetchedResultsControllerDelegate>
#property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;
- (void)performFetch;
#property (nonatomic) BOOL suspendAutomaticTrackingOfChangesInManagedObjectContext;
#property BOOL debug;
#end
CoreDataTableViewController.m
#interface CoreDataTableViewController()
#property (nonatomic) BOOL beganUpdates;
#end
#implementation CoreDataTableViewController
#pragma mark - Properties
#synthesize fetchedResultsController = _fetchedResultsController;
#synthesize suspendAutomaticTrackingOfChangesInManagedObjectContext = _suspendAutomaticTrackingOfChangesInManagedObjectContext;
#synthesize debug = _debug;
#synthesize beganUpdates = _beganUpdates;
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#pragma mark - Fetching
- (void)performFetch
{
if (self.fetchedResultsController) {
if (self.fetchedResultsController.fetchRequest.predicate) {
if (self.debug) NSLog(#"[%# %#] fetching %# with predicate: %#", NSStringFromClass([self class]), NSStringFromSelector(_cmd), self.fetchedResultsController.fetchRequest.entityName, self.fetchedResultsController.fetchRequest.predicate);
} else {
if (self.debug) NSLog(#"[%# %#] fetching all %# (i.e., no predicate)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), self.fetchedResultsController.fetchRequest.entityName);
}
NSError *error;
[self.fetchedResultsController performFetch:&error];
if (error) NSLog(#"[%# %#] %# (%#)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), [error localizedDescription], [error localizedFailureReason]);
} else {
if (self.debug) NSLog(#"[%# %#] no NSFetchedResultsController (yet?)", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
}
[self.tableView reloadData];
}
- (void)setFetchedResultsController:(NSFetchedResultsController *)newfrc
{
NSFetchedResultsController *oldfrc = _fetchedResultsController;
if (newfrc != oldfrc) {
_fetchedResultsController = newfrc;
newfrc.delegate = self;
if ((!self.title || [self.title isEqualToString:oldfrc.fetchRequest.entity.name]) && (!self.navigationController || !self.navigationItem.title)) {
self.title = newfrc.fetchRequest.entity.name;
}
if (newfrc) {
if (self.debug) NSLog(#"[%# %#] %#", NSStringFromClass([self class]), NSStringFromSelector(_cmd), oldfrc ? #"updated" : #"set");
[self performFetch];
} else {
if (self.debug) NSLog(#"[%# %#] reset to nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
[self.tableView reloadData];
}
}
}
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [[[self.fetchedResultsController sections] objectAtIndex:section] name];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
return [self.fetchedResultsController sectionForSectionIndexTitle:title atIndex:index];
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
return [self.fetchedResultsController sectionIndexTitles];
}
#pragma mark - NSFetchedResultsControllerDelegate
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext) {
[self.tableView beginUpdates];
self.beganUpdates = YES;
}
}
- (void)controller:(NSFetchedResultsController *)controller
didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex
forChangeType:(NSFetchedResultsChangeType)type
{
if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext)
{
switch(type)
{
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
}
- (void)controller:(NSFetchedResultsController *)controller
didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath
forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext)
{
switch(type)
{
case NSFetchedResultsChangeInsert:
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeMove:
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
if (self.beganUpdates) [self.tableView endUpdates];
}
- (void)endSuspensionOfUpdatesDueToContextChanges
{
_suspendAutomaticTrackingOfChangesInManagedObjectContext = NO;
}
- (void)setSuspendAutomaticTrackingOfChangesInManagedObjectContext:(BOOL)suspend
{
if (suspend) {
_suspendAutomaticTrackingOfChangesInManagedObjectContext = YES;
} else {
[self performSelector:#selector(endSuspensionOfUpdatesDueToContextChanges) withObject:0 afterDelay:0];
}
}
#end
And then last, but not least, I have the header file for my HomeViewController of which I implement CoreDataTableViewController. Hopefully this is useful in better explaining what I've got.
HomeViewController.h
#import <UIKit/UIKit.h>
#import <FacebookSDK/FacebookSDK.h>
#import "CoreDataTableViewController.h"
#interface HomeViewController : CoreDataTableViewController
#property (strong, nonatomic) UIManagedDocument *shindyDatabase;
#property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
#end

It's quite difficult to see what is going on, but, I think you need to do the following steps when the block has finished (I suppose this since I don't see any similar in your code).
execute the fetch request through the fetched controller
reload the data table
So,
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
// error handling here...
}
[yourTable reloadData];
Try and let me know. If doesn't work, try to edit a minimal question because there is too much code to follow.
Hope that helps.

I ended up figuring this, and many other Core Data issues I was having. I wasn't setting my managed object context to a store!! DUH!!!
self.managedObjectContext = [(AppDelegate *)[UIApplication sharedApplication].delegate managedObjectContext];
That did the trick. Everything began working after that.
I hope this is able to point out another duh to anybody out there forgetting the same thing I did!

Related

UICollectionViewController with NSFetchedResultsController broke, when update property of all objects, if controller sorted on this property

I have UICollectionViewController , It is collection view for some Objects at the map. So, I can update one item in fetched result (for example set LIKE for one of this Object). It is work well. I see changes immediately.
But if trying to update property "Distance" of all Objects, and same time controller are sorted on this property. Fetched result doesn't updated automatically. And then, while scrolling, reused Cells not updated, and I can see only Cells, that was on display, before I was started update property for all Objects. If I TouchUpInside first Cell in a row, for example it would be Object 123, controller open detail page of different Object, that Object, that should stay at this position after collectionView reloadData.
Incase I'm change Sort Descriptors from [fetchRequest setSortDescriptors:#[distanceAscending]];
to [fetchRequest setSortDescriptors:#[titleAscending]];
or [fetchRequest setSortDescriptors:#[titleAscending, distanceAscending]];
It is work well.
Incase I'm change property "Distance" of only one Object.
It is work well. Controller sort Cells as it should.
Incase I'm change property "Distance" of Objects not fetched this time.
It is work well.
Incase I'm close this controller, and open again [fetchRequest setSortDescriptors:#[distanceAscending]]; working as it should
I'm tying to reload CollectionViewController different ways,
[self.collectionView reloadData]; and reloadwithPredicateDefault. But same result.
I'm tying to change Managed Object Context NSPrivateQueueConcurrencyType and NSMainQueueConcurrencyType. But same result.
MapObjectCollectionViewController.h
#interface MapObjectCollectionViewController : UICollectionViewController
MapObjectCollectionViewController.m
#interface MapObjectCollectionViewController ()<NSFetchedResultsControllerDelegate>
#property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;
#property (strong, nonatomic) NSManagedObjectContext* managedObjectContext;
#property (strong, nonatomic) NSPredicate * predicate1;
#property (strong, nonatomic) NSPredicate * predicate2;
#pragma mark - Fetched results controller
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription* description =
[NSEntityDescription entityForName:#"MapObj"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:description];
_predicate1 = [NSPredicate predicateWithFormat:#"types.typeObjValue IN %#", self.selectionsTypes];
_predicate2 = [NSPredicate predicateWithFormat:#"wiFi >= %i", 0];
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:#[_predicate1, _predicate2]];
[fetchRequest setPredicate:predicate];
NSSortDescriptor* titleAscending = [[NSSortDescriptor alloc] initWithKey:#"title" ascending:YES];
NSSortDescriptor* distanceAscending = [[NSSortDescriptor alloc] initWithKey:#"distance" ascending:NO];
[fetchRequest setSortDescriptors:#[distanceAscending]];
// [fetchRequest setSortDescriptors:#[titleAscending, distanceAscending]];
NSFetchedResultsController *aFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return _fetchedResultsController;
}
-(void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath {
NSMutableDictionary *change = [[NSMutableDictionary alloc] init];
switch(type) {
case NSFetchedResultsChangeInsert:
change[#(type)] = newIndexPath;
break;
case NSFetchedResultsChangeDelete:
change[#(type)] = indexPath;
break;
case NSFetchedResultsChangeUpdate:
change[#(type)] = indexPath;
break;
case NSFetchedResultsChangeMove:
change[#(type)] = #[indexPath, newIndexPath];
break;
}
[_itemChanges addObject:change];
}
- (void)reloadwithPredicateDefault {
[NSFetchedResultsController deleteCacheWithName:nil];
self.fetchedResultsController = nil;
[self.fetchedResultsController performFetch:nil];
[self.collectionView reloadData];
}
#pragma mark - UICollectionViewDataSource
- (NSManagedObjectContext*) managedObjectContext {
if (!_managedObjectContext) {
_managedObjectContext = [[DataManager sharedManager] managedObjectContext];
}
return _managedObjectContext;
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller{
[self.collectionView performBatchUpdates:^{
for (NSDictionary *change in self->_sectionChanges) {
[change enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
NSFetchedResultsChangeType type = [key unsignedIntegerValue];
switch(type) {
case NSFetchedResultsChangeInsert:
[self.collectionView insertSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]]];
break;
case NSFetchedResultsChangeDelete:
[self.collectionView deleteSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]]];
break;
case NSFetchedResultsChangeMove:
break;
case NSFetchedResultsChangeUpdate:
break;
}
}];
}
for (NSDictionary *change in self->_itemChanges) {
[change enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
NSFetchedResultsChangeType type = [key unsignedIntegerValue];
switch(type) {
case NSFetchedResultsChangeInsert:
[self.collectionView insertItemsAtIndexPaths:#[obj]];
break;
case NSFetchedResultsChangeDelete:
[self.collectionView deleteItemsAtIndexPaths:#[obj]];
break;
case NSFetchedResultsChangeUpdate:
[self.collectionView reloadItemsAtIndexPaths:#[obj]];
break;
case NSFetchedResultsChangeMove:
[self.collectionView moveItemAtIndexPath:obj[0] toIndexPath:obj[1]];
break;
}
}];
}
} completion:^(BOOL finished) {
self->_sectionChanges = nil;
self->_itemChanges = nil;
}];
}
DataManager.h
#property (readonly, strong, nonatomic) NSManagedObjectContext *mainPrivateManagedObjectContext;
#property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
#property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
+ (DataManager*)sharedManager;
DataManager.m
#implementation DataManager
#synthesize mainPrivateManagedObjectContext = _mainPrivateManagedObjectContext;
#synthesize managedObjectContext = _managedObjectContext;
#synthesize managedObjectModel = _managedObjectModel;
#synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
+(DataManager*) sharedManager{
static DataManager* manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[DataManager alloc] init];
});
return manager;
}
#pragma mark - Core Data stack
- (NSManagedObjectModel *)managedObjectModel {
// The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"ProjectName" withExtension:#"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
// The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it.
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
// Create the coordinator and store
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"ProjectName.sqlite"];
NSError *error = nil;
// NSString *failureReason = #"There was an error creating or loading the application's saved data.";
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:#{NSMigratePersistentStoresAutomaticallyOption:#YES, NSInferMappingModelAutomaticallyOption:#YES} error:&error]) {
NSLog(#"error = %#", error);
// Report any error we got.
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]; //Удалить старую базу
[_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]; //Создать базу заново
}
return _persistentStoreCoordinator;
}
- (NSManagedObjectContext *)managedObjectContext {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
if (_managedObjectContext != nil) {
// . NSLog(#"get managedObjectContext");
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
return nil;
}
_mainPrivateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_mainPrivateManagedObjectContext setPersistentStoreCoordinator:coordinator];
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_managedObjectContext setParentContext:_mainPrivateManagedObjectContext];
// . NSLog(#"get return managedObjectContext");
return _managedObjectContext;
}
- (NSManagedObjectContext *)getContextForBGTask {
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[context setParentContext:self.managedObjectContext];
return context;
}
- (NSArray*) allMapObj {
NSFetchRequest* request = [[NSFetchRequest alloc] init];
NSEntityDescription* description =
[NSEntityDescription entityForName:#"MapObj"
inManagedObjectContext:self.managedObjectContext];
[request setEntity:description];
NSError* requestError = nil;
NSArray* resultArray = [self.managedObjectContext executeFetchRequest:request error:&requestError];
if (requestError) {
NSLog(#"%#", [requestError localizedDescription]);
}
return resultArray;
}
- (void)calculateDistanceWithCurrentLoaction:(CLLocation*) currentLoaction{
NSFetchRequest* request = [[NSFetchRequest alloc] init];
NSManagedObjectContext * bgcontext = [self getContextForBGTask];
NSEntityDescription* description =
[NSEntityDescription entityForName:#"MapObj"
inManagedObjectContext:bgcontext];
[request setEntity:description];
NSError* requestError = nil;
NSArray* resultArray = [bgcontext executeFetchRequest:request error:&requestError];
if (requestError) {
NSLog(#"%#", [requestError localizedDescription]);
}
for (MapObj *mapObject in resultArray) {
CLLocation *endLocation = [[CLLocation alloc] initWithLatitude:[mapObject.latitude doubleValue] longitude:[mapObject.longitude doubleValue]];
CLLocationDistance distance = [currentLoaction distanceFromLocation:endLocation];
mapObject.distance = [NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:#"%f", distance]];
}
[bgcontext updatedObjects];
[self saveContextForBGTask:bgcontext];
}
- (void)saveContextForBGTask:(NSManagedObjectContext *)bgTaskContext {
if (bgTaskContext.hasChanges) {
[bgTaskContext performBlockAndWait:^{
NSError *error = nil;
[bgTaskContext save:&error];
}];
// Save default context
[self saveDefaultContext:YES];
}
}
- (void)saveDefaultContext:(BOOL)wait {
if (_managedObjectContext.hasChanges) {
[_managedObjectContext performBlockAndWait:^{
// . NSLog(#"managed context = %#", _managedObjectContext);
NSError *error = nil;
[self->_managedObjectContext save:&error];
}];
}
void (^saveMainPrivateManagedObjectContext) (void) = ^{
NSError *error = nil;
[self->_mainPrivateManagedObjectContext save:&error];
};
if ([_mainPrivateManagedObjectContext hasChanges]) {
if (wait){
// . NSLog(#"main context = %#", _mainPrivateManagedObjectContext);
[_mainPrivateManagedObjectContext performBlockAndWait:saveMainPrivateManagedObjectContext];
} else {
[_mainPrivateManagedObjectContext performBlock:saveMainPrivateManagedObjectContext];
}
}
}
Error in console:
2019-09-28 12:35:14.951873+0400 ProjectName[15695:4020487] *** Assertion failure in -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:animator:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKitCore/UIKit-3698.140/UICollectionView.m:5972
CoreData: fault: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. attempt to perform an insert and a move to the same index path (<NSIndexPath: 0xa945f4d4afea737e> {length = 2, path = 0 - 4}) with userInfo (null)
What did I missed?
As a simple solution, was added:
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller{
[self.collectionView performBatchUpdates:^{
......
if(self->_itemChanges.count > 1){
[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
} else {
for (NSDictionary *change in self->_itemChanges) {
...
}
}
......
}

SearchBar in UITableView - sorting array issue

I'm trying to integrate search bar with my remote table view but it didn't work properly. There were no errors. I think I did some mistakes but I don't know which part so I need your help.
This is my full code:
//
// MasterViewController.m
#import "MasterViewController.h"
#import "DetailViewController.h"
#import "SDWebImage/UIImageView+WebCache.h"
static NSString *const kConsumerKey = #"a1SNULSPtp4eLQTsTXKKSgXkYB5H4CMFXmleFvqE";
#interface MasterViewController ()<UISearchDisplayDelegate>
#property (nonatomic, assign) NSInteger currentPage;
#property (nonatomic, assign) NSInteger totalPages;
#property (nonatomic, assign) NSInteger totalItems;
#property (nonatomic, assign) NSInteger maxPages;
#property (nonatomic, strong) NSMutableArray *photos;
#property (nonatomic, strong) NSMutableArray *searchResults;
#property (strong, nonatomic) IBOutlet UISearchBar *searchBar;
#end
#implementation MasterViewController
- (void)awakeFromNib {
[super awakeFromNib];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.photos = [NSMutableArray array];
self.searchResults = [NSMutableArray arrayWithCapacity:[self.photos count]];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self loadPhotos:self.currentPage];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table View
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (self.currentPage == self.maxPages
|| self.currentPage == self.totalPages
|| self.currentPage == self.totalPages
|| self.totalItems == self.photos.count) {
return self.photos.count;
}else if(tableView == self.searchDisplayController.searchResultsTableView){
return [self.searchResults count];
}
return self.photos.count + 1;
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
if (self.currentPage != self.maxPages && indexPath.row == [self.photos count] - 1 ) {
[self loadPhotos:++self.currentPage];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
if (indexPath.row == [self.photos count]) {
cell = [tableView dequeueReusableCellWithIdentifier:#"LoadingCell" forIndexPath:indexPath];
UIActivityIndicatorView *activityIndicator = (UIActivityIndicatorView *)[cell.contentView viewWithTag:100];
[activityIndicator startAnimating];
}else{
cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
if(tableView == self.searchDisplayController.searchResultsTableView) {
cell.textLabel.text = [self.searchResults objectAtIndex:indexPath.row];
}
NSDictionary *photoItem = self.photos[indexPath.row];
cell.textLabel.text = [photoItem objectForKey:#"name"];
if (![[photoItem objectForKey:#"description"] isEqual:[NSNull null]]) {
cell.detailTextLabel.text = [photoItem objectForKey:#"description"];
}
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:[photoItem objectForKey:#"image_url"]]
placeholderImage:[UIImage imageNamed:#"placeholder.jpg"]
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
if (error) {
NSLog(#"Error occured : %#", [error description]);
}
}];
}
return cell;
}
#pragma mark UISearchDisplay delegate
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
[self.searchResults removeAllObjects];
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:#"SELF contains[cd] %#",
searchText];
self.searchResults = [NSMutableArray arrayWithArray:[self.photos filteredArrayUsingPredicate:resultPredicate]];//[self.tableData filteredArrayUsingPredicate:resultPredicate];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
- (void)loadPhotos:(NSInteger)page {
NSString *apiURL = [NSString stringWithFormat:#"https://api.500px.com/v1/photos?feature=editors&page=%ld&consumer_key=%#",(long)page,kConsumerKey];
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:[NSURL URLWithString:apiURL]
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
if (!error) {
NSError *jsonError = nil;
NSMutableDictionary *jsonObject = (NSMutableDictionary *)[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&jsonError];
NSLog(#"%#",jsonObject);
[self.photos addObjectsFromArray:[jsonObject objectForKey:#"photos"]];
self.currentPage = [[jsonObject objectForKey:#"current_page"] integerValue];
self.totalPages = [[jsonObject objectForKey:#"total_pages"] integerValue];
self.totalItems = [[jsonObject objectForKey:#"total_items"] integerValue];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
}] resume];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
DetailViewController *vc = segue.destinationViewController;
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
vc.StoreList = [_photos objectAtIndex:indexPath.row];
}
#end
NSDictionary *photoItem = self.photos[indexPath.row];
and later
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"SELF contains[cd] %#", searchText];
self.searchResults = [NSMutableArray arrayWithArray:[self.photos filteredArrayUsingPredicate:resultPredicate]];
This looks like u try to sort with predicate not String but Dictionary.
Try :
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"SELF.name contains[c] %#", searchText];
Also i'd recommend u to read this code style and here some tutorial how to implement search
Regarding ur second question
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell;
if (indexPath.row == [self.photos count]) {
cell = [tableView dequeueReusableCellWithIdentifier:#"LoadingCell" forIndexPath:indexPath];
UIActivityIndicatorView *activityIndicator = (UIActivityIndicatorView *)[cell.contentView viewWithTag:100];
[activityIndicator startAnimating];
} else{
cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
if(tableView == self.searchDisplayController.searchResultsTableView) {
cell.textLabel.text = [self.searchResults[indexPath.row] valueForKey:#"name"] } else {
NSDictionary *photoItem = self.photos[indexPath.row];
cell.textLabel.text = [photoItem objectForKey:#"name"];
if (![[photoItem objectForKey:#"description"] isEqual:[NSNull null]]) {
cell.detailTextLabel.text = [photoItem objectForKey:#"description"];
}
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:[photoItem objectForKey:#"image_url"]]
placeholderImage:[UIImage imageNamed:#"placeholder.jpg"]
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
if (error) {
NSLog(#"Error occured : %#", [error description]);
}
}];
}
}
return cell;
}

Switching between NSFetchedResultsControllers using UISegmentedControl in UITableView

I been working in a project where i use core data to store some information, I have a UITableViewController with a UISegmentedControl and two NSFetchedResultsController because i need to fetched different data when i switch the segmented control.
When I first open the table view i can see the data corresponding to the first index of the segmented controller, but when i switch to the second index the data isn´t show and when i go back to the first index of the segmented control the data isn't show too.
This is the code
#import "TurnHistoryVC.h"
#import "AppDelegate.h"
#import "Turn.h"
#import "TurnInfoVC.h"
#import "Favorite.h"
#interface TurnHistoryVC ()
#property (nonatomic, retain) NSFetchedResultsController *currentFRC;
#property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
#property (nonatomic, strong) NSFetchedResultsController *favoritesResultsController;
#end
#implementation TurnHistoryVC
#synthesize fetchedResultsController = _fetchedResultsController;
#synthesize favoritesResultsController = _favoritesResultsController;
- (void)viewWillAppear:(BOOL)animated{
NSInteger *turn_id = [[NSUserDefaults standardUserDefaults] integerForKey: #"turn_noti_id"];
if (turn_id != 0) {
[self performSegueWithIdentifier:#"myTurn" sender:self];
} else {
id delegate = [[UIApplication sharedApplication] delegate];
self.managedObjectContext = [delegate managedObjectContext];
NSError *error;
self.currentFRC = self.fetchedResultsController;
if (![[self currentFRC] performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
}
[self.tableView reloadData];
[super viewWillAppear:animated];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Atrás" style:UIBarButtonItemStylePlain target:nil action:nil];
[self.tableView reloadData];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.currentFRC sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.currentFRC sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell.
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
Turn *turn = [self.currentFRC objectAtIndexPath:indexPath];
cell.textLabel.text = [NSString stringWithFormat:#"Turno no. %#", turn.turn_number];
cell.detailTextLabel.text = turn.companie_info;
cell.tag = [turn.turn_id intValue];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
// Display the authors' names as section headings.
// return #"";
return [[[self.currentFRC sections] objectAtIndex:section] name];
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
NSLog(#"cell id: %i", selectedCell.tag);
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
self.editButtonItem.title = #"Listo";
// [tableView ]
// Delete the managed object.
NSManagedObjectContext *context = [self.currentFRC managedObjectContext];
[context deleteObject:[self.currentFRC objectAtIndexPath:indexPath]];
NSError *error;
if (![context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
// abort();
}
}
}
//Metodo para cambiar el texto del boton para borrar favoritos
-(NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath {
return #"Elimiar";
}
-(void)toggleEdit{
[self.tableView setEditing:!self.tableView.editing animated:YES];
if (self.tableView.editing){
[self.navigationItem.leftBarButtonItem setTitle:#"Listo"];
}else{
[self.navigationItem.leftBarButtonItem setTitle:#"Editar"];
}
}
#pragma mark - Navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([sender isKindOfClass:[UITableViewCell class]]){
if ([segue.destinationViewController isKindOfClass:[TurnInfoVC class]]){
TurnInfoVC *turnInfoVC = segue.destinationViewController;
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
UITableViewCell *selectedCell = [self.tableView cellForRowAtIndexPath:indexPath];
turnInfoVC.turn_id = selectedCell.tag;
}
}
}
#pragma mark - Cambio de turnos pendientes a finalizados
- (IBAction)btnTurnTypes:(UISegmentedControl *)sender {
NSError *error;
NSLog(#"cambie de tab 0");
switch (sender.selectedSegmentIndex) {
case 0:
{
self.currentFRC = self.fetchedResultsController;
[self.tableView reloadData];
}
break;
case 1:
{
self.currentFRC = self.favoritesResultsController;
[self.tableView reloadData];
NSLog(#"cambie de tab 1");
}
break;
default:
break;
}
[self.tableView reloadData];
}
#pragma mark - Fetched results controller
/*
Returns the fetched results controller. Creates and configures the controller if necessary.
*/
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
}
NSLog(#"turnos pendientes");
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Turn"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"turn_confirmation==0 AND turn_status==1"];
fetchRequest.predicate = predicate;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Turn" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Create the sort descriptors array.
NSSortDescriptor *categoryDescriptor = [[NSSortDescriptor alloc] initWithKey:#"companie_info" ascending:YES];
NSSortDescriptor *companyDescriptor = [[NSSortDescriptor alloc] initWithKey:#"turn_number" ascending:YES];
NSArray *sortDescriptors = #[categoryDescriptor, companyDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"companie_info" cacheName:#"Root"];
_fetchedResultsController.delegate = self;
self.currentFRC = _fetchedResultsController;
return _fetchedResultsController;
}
- (NSFetchedResultsController *)favoritesResultsController {
if (_favoritesResultsController != nil) {
}
NSLog(#"turnos finalizados");
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Turn"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"turn_confirmation==0 AND turn_status==0"];
fetchRequest.predicate = predicate;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Turn" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Create the sort descriptors array.
NSSortDescriptor *categoryDescriptor = [[NSSortDescriptor alloc] initWithKey:#"companie_info" ascending:YES];
NSSortDescriptor *companyDescriptor = [[NSSortDescriptor alloc] initWithKey:#"turn_number" ascending:YES];
NSArray *sortDescriptors = #[categoryDescriptor, companyDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
// Create and initialize the fetch results controller.
_favoritesResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"companie_info" cacheName:#"Root"];
_favoritesResultsController.delegate = self;
self.currentFRC = _favoritesResultsController;
return _favoritesResultsController;
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
NSLog(#"actualizando fuera del if");
// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
if (controller == self.currentFRC) {
NSLog(#"actualizando dentro del if");
[self.tableView beginUpdates];
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:#[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView insertRowsAtIndexPaths:#[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller has sent all current change notifications, so tell the table view to process all updates.
[self.tableView endUpdates];
}
#end
I already try this solution, but i can't get it to work, thanks

UITableView stopped loading data in IOS7 and Xcode5

I have a simple view which has been successfully loading a table of data from CoreData for the last 18 months. It even works now on devices that have been upgraded to IOS 7. But when I upgraded to Xcode5 and run through the IOS7 3.5inch retina simulator my table is always empty. I have pasted my code below and I can confirm that the fetchRequests are returning the data because I can see this in the NSLog outputs. But why has be table stopped populating the cells?
I feel really stupid because I just cannot figure this one out…
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"listData count in numberOFRowsInSection is: %i", listData.count);
return [self.listData count];
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
Sessions *info = [_fetchedResultsController objectAtIndexPath:indexPath];
NSLog(#"info content is: %#", info.sport);
//Format cell data ready to be displayed
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"EE, dd LLL yyyy"];
NSString *dateString = [dateFormat stringFromDate:info.date];
NSNumber *dist1Nbr = info.dist1;
int dist1Int = [dist1Nbr integerValue];
float distIntKorM = ([dist1Nbr integerValue])/1000;
NSString *dist1StrMeters = [[NSString alloc] initWithFormat:#"%i", dist1Int];
NSString *dist1StrKorM = [[NSString alloc] initWithFormat:#"%.01f", distIntKorM];
//Select image to display
if ([info.sport isEqualToString:#"Run"]) {
UIImage *image = [UIImage imageNamed:#"trainers-15x10.png"];
cell.imageView.image = image;
cell.textLabel.text = [[NSString alloc] initWithFormat:#"%#: (%#),", dateString, info.sport];
cell.detailTextLabel.text = [[NSString alloc] initWithFormat:#"Type: %#, Dist: %#", info.sessiontype, dist1StrKorM];
NSLog(#"Cell text for Runs shoudl be: %#", cell.textLabel.text);
} else if ([info.sport isEqualToString:#"Other"]) {
UIImage *image = [UIImage imageNamed:#"weights-15x10.png"];
cell.imageView.image = image;
cell.textLabel.text = [[NSString alloc] initWithFormat:#"%#: (%#),", dateString, info.sport];
cell.detailTextLabel.text = [[NSString alloc] initWithFormat:#"Type: %#, Dist: %#", info.sessiontype, dist1StrKorM];
} else if ([info.sport isEqualToString:#"Swim"]) {
UIImage *image = [UIImage imageNamed:#"goggles-15x10.png"];
cell.imageView.image = image;
cell.textLabel.text = [[NSString alloc] initWithFormat:#"%#: (%#),", dateString, info.sport];
cell.detailTextLabel.text = [[NSString alloc] initWithFormat:#"Type: %#, Dist: %#m", info.sessiontype, dist1StrMeters];
NSLog(#"Cell text for Swims shoudl be: %#", cell.textLabel.text);
} else if ([info.sport isEqualToString:#"Cycle"]) {
UIImage *image = [UIImage imageNamed:#"bike-15x10.png"];
cell.imageView.image = image;
cell.textLabel.text = [[NSString alloc] initWithFormat:#"%#: (%#),", dateString, info.sport];
cell.detailTextLabel.text = [[NSString alloc] initWithFormat:#"Type: %#, Dist: %#", info.sessiontype, dist1StrKorM];
} else if ([info.sport isEqualToString:#"Brick"]) {
UIImage *image = [UIImage imageNamed:#"brick-15x10.png"];
cell.imageView.image = image;
cell.textLabel.text = [[NSString alloc] initWithFormat:#"%#: (%#),", dateString, info.sport];
cell.detailTextLabel.text = [[NSString alloc] initWithFormat:#"Type: %#, Dist: %#", info.sessiontype, dist1StrKorM];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"editSession";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
// this cell backgrond colour alternating works!
UIView *bgColor = [cell viewWithTag:100];
if (!bgColor) {
CGRect frame = CGRectMake(0, 0, 320, 50);
bgColor = [[UIView alloc] initWithFrame:frame];
bgColor.tag = 100;
[cell addSubview:bgColor];
[cell sendSubviewToBack:bgColor];
}
if (indexPath.row % 2 == 0) {
bgColor.backgroundColor = [UIColor colorWithRed:233.0/255.0 green:233.0/255.0 blue:233.0/255.0 alpha:1.0];
} else {
bgColor.backgroundColor = [UIColor clearColor]; }
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (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;
if (_context == nil)
{
_context = [(SGK_T4T_01AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSLog(#"After managedObjectContext: %#", _context);
}
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1);
} else {
NSLog(#"After fetchedResultsController: %#", _fetchedResultsController);
//NSLog(#"After managedObjectContext: %#", _fetchedResultsController);
}
self.title = #"Sessions";
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
[self setListData:nil];
[self setSelectedSession:nil];
[self setSessionSport:nil];
//[self setRecordCount:nil];
[self setFetchedResultsController:nil];
[self setContext:nil];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
SGK_T4T_01AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSEntityDescription *entityDiscription = [NSEntityDescription entityForName:#"Sessions" inManagedObjectContext:context];
//NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"refid" ascending:YES];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDiscription];
NSError *error;
NSArray *objects = [context executeFetchRequest:request error:&error];
if (objects == nil) {
NSLog(#"The fetch request returned an array == nil");
} else {
NSLog(#"The fetch request returned an array!!!");
NSLog(#"objects contents is: %#", objects);
NSLog(#"objects count = %i", [objects count]);
listData = objects;
NSLog(#"listData count = %i", [listData count]);
//NSUInteger *recordCount = [objects count];
recordCount = [objects count];
}
//reload tableView:dataSource from CoreData when view reappears...
if (_context == nil)
{
_context = [(SGK_T4T_01AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSLog(#"After managedObjectContext: %#", _context);
}
NSError *error1;
if (![[self fetchedResultsController] performFetch:&error1]) {
NSLog(#"Unresolved error %#, %#", error1, [error1 userInfo]);
exit(-1);
} else {
NSLog(#"viewWillAppear: fetchedResultsController: %#", _fetchedResultsController);
}
//end of reload tableView:dataSource from CoreData when view reappears...
[self.tableView reloadData];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
//[self setFetchedResultsController:nil];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
//[self setFetchedResultsController:nil];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
//#warning Potentially incomplete method implementation.
// Return the number of sections.
return 1;
}
/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
*/
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[_context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];
NSError *error = nil;
if (![_context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
} else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
[self.tableView reloadData];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
{
if (type == NSFetchedResultsChangeDelete) {
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
NSUInteger index = indexPath.row;
//NSLog(#" Prep4Seg indexPath.row = %u", index);
//NSLog(#" Prep4Seg recordCount = %u", recordCount);
/*
Subtract the row index from the row count to get the correct position in the Array (because I am sorting on date order so tableCell position 0 is position 2 in the Array (if there are 3 only items in the table and array) so if you don't invert the indexPath you end up passing the last item instead of the first or second last item instead of the second and so on...
*/
NSUInteger arrayIndex = (recordCount-index-1);
//NSLog(#" Prep4Seg arrayIndex = %u", arrayIndex);
selectedSession = [listData objectAtIndex:arrayIndex];
//NSLog(#"listData = %#", listData);
//NSLog(#"SelectedSession = %#", selectedSession);
NSNumber *refId = [selectedSession valueForKey:#"refid"];
NSString *refIdToSend = [[NSString alloc] initWithFormat:#"%#", refId];
NSLog(#"Prep4Seg in tableView: refIdToSend = %#", refIdToSend);
if ([segue.identifier isEqualToString:#"editSession"]) {
SGK_T4T_EditSessionDetail *editSessionDetail = segue.destinationViewController;
editSessionDetail.delegate = (id)self;
editSessionDetail.returnFromDatePickerView = [[NSString alloc] initWithFormat:#"no"];
editSessionDetail.recedIndex = refIdToSend;
}
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller has sent all current change notifications, so tell the table view to process all updates.
[self.tableView endUpdates];
}
And my log output is:
2013-11-09 16:04:04.034 trainForTri copy[6509:a0b] After managedObjectContext: <NSManagedObjectContext: 0xb589bc0>
2013-11-09 16:04:04.036 trainForTri copy[6509:a0b] After fetchedResultsController: <NSFetchedResultsController: 0xb5e2c40>
2013-11-09 16:04:04.040 trainForTri copy[6509:a0b] The fetch request returned an array!!!
2013-11-09 16:04:04.041 trainForTri copy[6509:a0b] objects contents is: (
"<NSManagedObject: 0xb5c8570> (entity: Sessions; id: 0xb5ac970 <x-coredata://B93EE0DE-E6FA-491C-9C8F-23692A36DD0C/Sessions/p1> ; data: <fault>)",
"<NSManagedObject: 0xb5d6320> (entity: Sessions; id: 0xb58a2a0 <x-coredata://B93EE0DE-E6FA-491C-9C8F-23692A36DD0C/Sessions/p2> ; data: <fault>)",
"<NSManagedObject: 0xb5d0370> (entity: Sessions; id: 0xb5dc270 <x-coredata://B93EE0DE-E6FA-491C-9C8F-23692A36DD0C/Sessions/p3> ; data: <fault>)",
"<NSManagedObject: 0xb5e3dd0> (entity: Sessions; id: 0xb5ebcc0 <x-coredata://B93EE0DE-E6FA-491C-9C8F-23692A36DD0C/Sessions/p4> ; data: <fault>)",
"<NSManagedObject: 0xb585e40> (entity: Sessions; id: 0xb5e4e40 <x-coredata://B93EE0DE-E6FA-491C-9C8F-23692A36DD0C/Sessions/p5> ; data: <fault>)"
)
2013-11-09 16:04:04.041 trainForTri copy[6509:a0b] objects count = 5
2013-11-09 16:04:04.042 trainForTri copy[6509:a0b] listData count = 5
2013-11-09 16:04:04.043 trainForTri copy[6509:a0b] viewWillAppear: fetchedResultsController: <NSFetchedResultsController: 0xb5a01e0>
2013-11-09 16:04:04.043 trainForTri copy[6509:a0b] listData count in numberOFRowsInSection is: 5
OK, found the issue with this. Eventually after almost wanting to throw my Macbook out of the window in frustration with Xcode5 I shut everything down and restarted. when I opened Xcode, built and ran my App it lo longer loaded an empty table - it now produced an error which indicated that there was something wrong with my NSFetchedResultsController. I traced it to an issue with the cache not clearing (or not being mutable) - so I removed the cache name and set it to nil and everything is working again. I am not sure if this is an IOS7/Xcode5 issue but it has not been a problem until this upgrade. For anyone searching for a similar fix here is the offending line of code:
NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:_context sectionNameKeyPath:nil cacheName:#"Root"];
And this is the version that works!
NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:_context sectionNameKeyPath:nil cacheName:nil];
I hope I'm not the only one ever to struggle with this and therefore just a thickie!

Getting a crash relating to deallocated instance

I am getting a crash at line [selectedSession release]; in dealloc:
Default [NSCheapMutableString release]: message sent to deallocated instance
I don't understand why isn't this normal to put in dealloc?
All current code below:
LogViewController
#implementation LogViewController
#synthesize fetchedResultsController = __fetchedResultsController;
#synthesize managedObjectContext;
#synthesize logArray;
#synthesize logTableView;
#synthesize imageView;
#synthesize session;
#synthesize selectedSession;
- (void)dealloc
{
[logArray release];
[logTableView release];
[session release];
[__fetchedResultsController release];
[managedObjectContext release];
[imageView release];
[selectedSession release];
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
self.logTableView.rowHeight = 47;
[super viewDidLoad];
self.navigationItem.title = #"Log";
logTableView.backgroundColor = [UIColor clearColor];
logTableView.separatorColor = [UIColor grayColor];
self.navigationController.navigationBar.tintColor = [UIColor colorWithRed:24/255.0 green:83/255.0 blue:170/255.0 alpha:1.0];
self.logArray = [[NSArray alloc]initWithObjects:#"Today", #"Previous", #"Past Week", #"Past Month", #"All Workouts", nil];
if (managedObjectContext == nil)
{
self.managedObjectContext = [(CurlAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
}
- (void)viewDidUnload
{
self.logTableView = nil;
self.fetchedResultsController = nil;
self.imageView = nil;
self.managedObjectContext = nil;
[super viewDidUnload];
}
- (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);
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.logArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
TDBadgedCell *cell = [[[TDBadgedCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(TDBadgedCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
cell.textLabel.textColor = [UIColor blackColor];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.text = [logArray objectAtIndex:indexPath.row];
cell.backgroundColor = [UIColor clearColor];
cell.imageView.image = [UIImage imageNamed:#"17-bar-chart.png"];
UIImageView *myImageView = nil;
myImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"silvercell5.png"]];
[cell setBackgroundView:myImageView];
[myImageView release];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"MMM d, y"];
NSDate *date = nil;
if (indexPath.row == 0)
{
date = [NSDate date];
NSString *dateString = [dateFormatter stringFromDate:date];
cell.badgeString = dateString;
}
else if (indexPath.row == 1)
{
if ([[self.fetchedResultsController fetchedObjects]count] > 1)
{
self.session = [[self.fetchedResultsController fetchedObjects]objectAtIndex:1];
NSDate *date = self.session.timeStamp;
NSString *dateString = [dateFormatter stringFromDate:date];
cell.badgeString = dateString;
}
else
{
cell.badgeString = #"None";
}
}
else if (indexPath.row > 1)
{
cell.badgeString = [NSString stringWithFormat:#"%i", [[self.fetchedResultsController fetchedObjects]count]];
}
cell.badgeColor = [UIColor colorWithRed:24/255.0 green:83/255.0 blue:170/255.0 alpha:1.0];
[dateFormatter release];
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if (indexPath.row == 0 || indexPath.row == 1)
{
SessionViewController *detailViewController = [[SessionViewController alloc] initWithNibName:#"SessionViewController" bundle:nil];
detailViewController.title = [logArray objectAtIndex: indexPath.row];
self.selectedSession = (Session *)[__fetchedResultsController objectAtIndexPath:indexPath];
detailViewController.selectedSession = self.selectedSession;
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"MMM d, y"];
NSString *dateString = [dateFormatter stringFromDate:selectedSession.timeStamp];
detailViewController.title = dateString;
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
[dateFormatter release];
}
else
{
LogResultsViewController *detailViewController = [[LogResultsViewController alloc] initWithNibName:#"LogResultsTableViewController" bundle:nil];
detailViewController.title = [logArray objectAtIndex: indexPath.row];
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
}
}
#pragma mark - Fetched results controller
- (NSFetchedResultsController *)fetchedResultsController
{
if (fetchedResultsController != nil)
{
return fetchedResultsController;
}
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Session" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0] ;
NSDate *today = [NSDate date];
NSDate *thisWeek = [today dateByAddingTimeInterval: -604800.0];
NSDate *thisMonth = [today dateByAddingTimeInterval: -2629743.83]; // Use NSCalendar for
if (indexPath.row ==2)
{
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"(date >= %#) AND (date <= %#)", thisWeek, today]];
}
else if (indexPath.row ==3)
{
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"(date >= %#) AND (date <= %#)", thisMonth, today]];
}
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"timeStamp" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects: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:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error])
{
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
NSLog(#"Number of Objects = %i",
[[fetchedResultsController fetchedObjects] count]);
return fetchedResultsController;
NSLog(#"Number of Objects = %i",
[[fetchedResultsController fetchedObjects] count]);
}
#pragma mark - Fetched results controller delegate
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[self.logTableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
switch(type)
{
case NSFetchedResultsChangeInsert:
[self.logTableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.logTableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
UITableView *tableView = self.logTableView;
switch(type)
{
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.logTableView endUpdates];
}
LogResultsViewController
#implementation LogResultsViewController
#synthesize fetchedResultsController = __fetchedResultsController;
#synthesize managedObjectContext;
#synthesize resultsTableView;
#synthesize selectedSession;
- (void)dealloc
{
[__fetchedResultsController release];
[managedObjectContext release];
[selectedSession release];
[resultsTableView release];
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.resultsTableView.separatorColor = [UIColor grayColor];
self.resultsTableView.rowHeight = 50;
[self managedObjectContext];
}
- (NSManagedObjectContext *)managedObjectContext
{
if (managedObjectContext != nil)
{
return managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [(CurlAppDelegate *)[[UIApplication sharedApplication] delegate] persistentStoreCoordinator];
if (coordinator != nil)
{
managedObjectContext = [[NSManagedObjectContext alloc] init];
[managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return managedObjectContext;
}
- (void)viewDidUnload
{
[super viewDidUnload];
self.managedObjectContext = nil;
self.fetchedResultsController = nil;
self.resultsTableView = nil;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
Session *session = (Session *)[__fetchedResultsController objectAtIndexPath:indexPath];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"eeee, MMM d, y"];
NSString *dateString = [dateFormatter stringFromDate:session.timeStamp];
NSDate *lastDate = session.timeStamp;
NSDate *todaysDate = [NSDate date];
NSTimeInterval lastDiff = [lastDate timeIntervalSinceNow];
NSTimeInterval todaysDiff = [todaysDate timeIntervalSinceNow];
NSTimeInterval dateDiff = todaysDiff-lastDiff;
NSTimeInterval dayDifference = dateDiff/86400;
int days = (int) dayDifference;
NSLog(#"%i days",days);
cell.textLabel.text = dateString;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.detailTextLabel.text = [NSString stringWithFormat: #"%i days ago", days];
cell.detailTextLabel.textColor = [UIColor colorWithRed:24/255.0 green:83/255.0 blue:170/255.0 alpha:1.0];
cell.imageView.image = [UIImage imageNamed:#"11-clock.png"];
self.resultsTableView.tableFooterView = [[[UIView alloc] init] autorelease];
UIImageView *myImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"silvercell3.png"]];
[cell setBackgroundView:myImageView];
[dateFormatter release];
[myImageView release];
}
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
// Delete the managed object for the given index path
NSManagedObjectContext *context = [__fetchedResultsController managedObjectContext];
[context deleteObject:[__fetchedResultsController objectAtIndexPath:indexPath]];
// Commit the change.
NSError *error = nil;
// Update the array and table view.
if (![managedObjectContext save:&error])
{
// Handle the error.
}
//[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES];
}
}
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
SessionViewController *sessionViewController = [[SessionViewController alloc] initWithNibName:#"SessionViewController" bundle:nil];
selectedSession = (Session *)[__fetchedResultsController objectAtIndexPath:indexPath];
sessionViewController.selectedSession = self.selectedSession;
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"MMM d, y"];
NSString *dateString = [dateFormatter stringFromDate:selectedSession.timeStamp];
sessionViewController.title = dateString;
[self.navigationController pushViewController:sessionViewController animated:YES];
[sessionViewController release];
[dateFormatter release];
}
#pragma mark - Fetched results controller
- (NSFetchedResultsController *)fetchedResultsController
{
if (fetchedResultsController != nil)
{
return fetchedResultsController;
}
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Session" 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:#"timeStamp" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects: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:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
NSError *error = nil;
if (![fetchedResultsController performFetch:&error])
{
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return fetchedResultsController;
}
#pragma mark - Fetched results controller delegate
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[self.resultsTableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
switch(type)
{
case NSFetchedResultsChangeInsert:
[self.resultsTableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.resultsTableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
UITableView *tableView = self.resultsTableView;
switch(type)
{
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.resultsTableView endUpdates];
}
SessionViewController
#implementation SessionViewController
#synthesize exerciseArray;
#synthesize selectedSession;
- (void)dealloc
{
[exerciseArray release];
[selectedSession release];
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
-(IBAction)showActionSheet {
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:#"Share" delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:nil otherButtonTitles:#"Twitter",#"Facebook",nil];
[actionSheet showInView:self.tabBarController.view];
[actionSheet release];
}
- (void)tweet
{
SHKItem *aTweet = [SHKItem text:[NSString stringWithFormat: #"Twitter: testing 1,2,3."]];
[SHKTwitter shareItem:aTweet];
}
- (void)facebook
{
SHKItem *post = [SHKItem text: [NSString stringWithFormat: #"Facebook: testing 1,2,3."]];
// post.URL = [NSURL URLWithString:#"http://sugarrush-app.com/"];
[SHKFacebook shareItem:post];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
UIBarButtonItem *actionButton = [[UIBarButtonItem alloc] initWithTitle:#"Share" style:UIBarButtonItemStylePlain target:self action:#selector(showActionSheet)];
self.navigationItem.rightBarButtonItem = actionButton;
[actionButton release];
NSSet *exercises = [self.selectedSession valueForKey:#"exercises"];
NSArray *sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"timeStamp" ascending:YES]];
NSArray *sorted = [exercises sortedArrayUsingDescriptors:sortDescriptors];
self.exerciseArray = sorted;
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [exerciseArray count];
}
- (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];
}
Exercise *exercise = (Exercise *)[exerciseArray objectAtIndex:indexPath.row];
cell.textLabel.text = exercise.name;
return cell;
}
#end
The only objects you should be releasing or setting to nil in viewDidUnload are like buttons, labels, textBoxes etc.
When you do self.selectedSession = nil, for example, it is calling the release for that method; that is the point of properties, they handle the allocs and releases.
You posted a tableView:didSelectRowAtIndexPath:, but I don't think you told us which class that was in.
In any case, that method seems to have a bug:
selectedSession = (Session *)[__fetchedResultsController objectAtIndexPath:indexPath];
sessionViewController.selectedSession = self.selectedSession;
I think on the first line, you meant to say self.selectedSession, assuming you go on to release selectedSession in this class' dealloc. (This may not be your only issue)
EDIT:
You should set properties to nil in viewDidUnload if and only if those properties were set as a result of the loading of the view from your nib, or were set in loadView, or in viewDidLoad. The reason you're doing this is because viewDidLoad or loadView may be called more than once during the lifetime of the view controller.
EDIT:
Check that you are using the property when setting fetchedResultsController and make sure you aren't setting that to nil on viewDidUnload.
Dont set selectedSession and exerciseArray to nil in viewDidUnload.
Since you're doing that, it will crash in the dealloc as you're trying to release nil objects.
Also, before you release arrays, it's always a good idea to call [array removeAllObjects].
in your viewDidLoad method you are setting self.selectedSession and self.exerciseArray to nil. You should only set interface outlets to nil in viewDidLoad. But this isn't the cause of your crash because by the time you get to dealloc, you are just sending release to nil.
you might want to look into the contents of the selectedSession Object. you might be over releasing an NSString in one of it's members