I have grouped table with custom UITableViewCell. I am calling a web service using AFNetworking and storing them in Core Data Entity. I display the data from Core Data table by using NSFetchedResultsViewController. I also have search on the tableview. For some reason, NSFetchedResultsController
returns no data even though there is a data in the Core Data Entity. Can you help in resolving this issue? here is my code.
#import "MasterViewController.h"
#import "DetailViewController.h"
@interface MasterViewController ()
@property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController;
@end
@implementation MasterViewController
@synthesize fetchedResultsController;
- (void)viewDidLoad {
[super viewDidLoad];
appDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate];
}
-(void)viewDidAppear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] postNotificationName:@"RackStatusViewLoaded" object:self];
self.navigationItem.title = [defaults objectForKey:@"loggedInUserSelectedStoreName"];
NSString *activeStockTakeRequestPath = [NSString stringWithFormat:@"%@/stocktake/store/%@/activestocktake",[appDelegate baseUrl],[defaults objectForKey:@"loggedInUserSelectedStoreId"]];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:[appDelegate baseUrl]]];
NSMutableURLRequest *request = [httpClient requestWithMethod:@"GET"
path:activeStockTakeRequestPath
parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSInteger statusCode = operation.response.statusCode;
// NSLog(@"AFNetowrking Response: %@", [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]);
NSLog(@"AF Status Code %d",statusCode);
NSError *error;
NSMutableDictionary *returnedDict = [NSJSONSerialization JSONObjectWithData:responseObject options:kNilOptions error:&error];
if (statusCode==200)
{
NSLog(@"Status 200");
[defaults setBool:true forKey:@"isActiveStockTakeForCurrentStore"];
[defaults setObject:returnedDict[@"id"] forKey:@"ActiveStockTakeid"];
[defaults setObject:returnedDict[@"name"] forKey:@"ActiveStockTakeName"];
[defaults setObject:returnedDict[@"onDate"] forKey:@"ActiveStockTakeDate"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"setTitleForCurrentActiveStock" object:self];
NSLog(@"isActiveStock? %@",[defaults objectForKey:@"isActiveStockTakeForCurrentStore"]);
NSLog(@"ActiveStockTakeid? %@",[defaults objectForKey:@"ActiveStockTakeid"]);
NSLog(@"ActiveStockTakeName? %@",[defaults objectForKey:@"ActiveStockTakeName"]);
NSLog(@"ActiveStockTakeDate? %@",[defaults objectForKey:@"ActiveStockTakeDate"]);
[defaults synchronize];
self.fetchedResultsController = nil;
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"RackStockTakeStatus"];
[self requestLocationDataWithStatus];
NSError *error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
if (error != nil) {
//Deal with failure
}
else {
NSLog(@"Data from Core Data %@", results);
NSLog(@"Count From Core Data Entity : %d",[results count]);
}
[self performFetchForItemsInStore];
[self.tableView reloadData];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}
}];
[operation start];
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
appDelegate.selectedRackForTakeStock = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSLog(@"Selected Rack %@",appDelegate.selectedRackForTakeStock);
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[defaults setObject:cell.textLabel.text forKey:@"selectedRackInTakeStock"];
[defaults synchronize];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
NSLog(@"Number of Rows: %d",[sectionInfo numberOfObjects]);
return [sectionInfo numberOfObjects];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
NSLog(@"Number of Sections: %d",[[self.fetchedResultsController sections] count]);
return [[self.fetchedResultsController sections] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = @"takeStockCell";
UITableViewCell * cell = [self.tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
RackStockTakeStatus *rackStockTakeStatus = [self.fetchedResultsController objectAtIndexPath:indexPath];
UILabel *rackName, *status, *user, *progress;
rackName = (UILabel *)[cell viewWithTag:1];
rackName.text = rackStockTakeStatus.locName;
status = (UILabel *)[cell viewWithTag:2];
status.text = rackStockTakeStatus.status;
status.text = rackStockTakeStatus.status;
progress = (UILabel *)[cell viewWithTag:3];
if (rackStockTakeStatus.percentCompleted!=nil)
{
progress.text = [NSString stringWithFormat:@"Progress: %@%%",rackStockTakeStatus.percentCompleted];
}
if (rackStockTakeStatus.stockTakeByUser!=nil)
{
user = (UILabel *)[cell viewWithTag:4];
user.text = rackStockTakeStatus.stockTakeByUser;
}
}
- (void)requestLocationDataWithStatus {
NSLog(@"Entering requestDataItemsForStore");
NSString *requestPath = [NSString stringWithFormat:@"/stocktake/stocktake/%@/usr/1/locwithstatus",[defaults objectForKey:@"loggedInUserSelectedStoreId"]];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:[appDelegate baseUrl]]];
NSMutableURLRequest *request = [httpClient requestWithMethod:@"GET"
path:requestPath
parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSInteger statusCode = operation.response.statusCode;
// NSLog(@"AFNetowrking Response: %@", [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]);
NSLog(@"AF Status Code %d",statusCode);
NSError *error;
NSMutableDictionary *returnedDict = [NSJSONSerialization JSONObjectWithData:responseObject options:kNilOptions error:&error];
if (statusCode==200)
{
for (NSDictionary *rackStockTakeStatus in returnedDict) {
RackStockTakeStatus *rackStockTakeStatusObj;
rackStockTakeStatusObj.stockTakeLocId = rackStockTakeStatus[@"stockTakeLocId"];
rackStockTakeStatusObj.stockTakeUuid = rackStockTakeStatus[@"stockTakeUuid"];
rackStockTakeStatusObj.locId = rackStockTakeStatus[@"locId"];
rackStockTakeStatusObj.locName = rackStockTakeStatus[@"locName"];
rackStockTakeStatusObj.status = rackStockTakeStatus[@"status"];
rackStockTakeStatusObj.stockTakeByUser = rackStockTakeStatus[@"stockTakeByUser"];
rackStockTakeStatusObj.stockTakeByUserId = rackStockTakeStatus[@"stockTakeByUserId"];
rackStockTakeStatusObj.beginTime = rackStockTakeStatus[@"beginTime"];
[self saveDataToCoreDataEntity:rackStockTakeStatusObj];
}
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
[operation start];
}
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"RackStockTakeStatus" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSSortDescriptor *rackNameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"locName" ascending:YES];
NSArray *sortDescriptors = @[rackNameDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];
fetchedResultsController.delegate = self;
return fetchedResultsController;
}
- (void)fetchItemsForStore {
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"RackStockTakeStatus" inManagedObjectContext:context];
[request setEntity:entity];
NSError *error;
[context executeFetchRequest:request error:&error];
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
NSLog(@"Begin Updates");
[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 {
NSLog(@"End Updates");
[self.tableView endUpdates];
}
-(void)performFetchForItemsInStore
{
NSLog(@"Entering performFetchForItemsInStore");
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
}
}
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller
{
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSLog(@"Cancelled - searchDisplayControllerDidEndSearch");
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"RackStockTakeStatus" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSSortDescriptor *rackNameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"locName" ascending:YES];
NSArray *sortDescriptors = @[rackNameDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];
fetchedResultsController.delegate = self;
[self performFetchForItemsInStore];
[self.tableView reloadData];
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
NSInteger searchOption = controller.searchBar.selectedScopeButtonIndex;
return [self searchDisplayController:controller shouldReloadTableForSearchString:searchString searchScope:searchOption];
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
NSString * searchString = controller.searchBar.text;
return [self searchDisplayController:controller shouldReloadTableForSearchString:searchString searchScope:searchOption];
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString*)searchString searchScope:(NSInteger)searchOption {
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"locName contains[cd] %@", searchString];
[self.fetchedResultsController.fetchRequest setPredicate:predicate];
[self.fetchedResultsController.fetchRequest setFetchLimit:100];
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
TODO: "Error Handling / Error message";
}
return YES;
}
-(void)saveDataToCoreDataEntity:(RackStockTakeStatus *) rackStockTakeStatus
{
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSManagedObject *rackStockTakeStatusManagedObject = [NSEntityDescription insertNewObjectForEntityForName:@"RackStockTakeStatus" inManagedObjectContext:context];
[rackStockTakeStatusManagedObject setValue:rackStockTakeStatus.stockTakeLocId forKey:@"stockTakeLocId"];
[rackStockTakeStatusManagedObject setValue:rackStockTakeStatus.stockTakeUuid forKey:@"stockTakeUuid"];
[rackStockTakeStatusManagedObject setValue:rackStockTakeStatus.locId forKey:@"locId"];
[rackStockTakeStatusManagedObject setValue:rackStockTakeStatus.locName forKey:@"locName"];
[rackStockTakeStatusManagedObject setValue:rackStockTakeStatus.status forKey:@"status"];
[rackStockTakeStatusManagedObject setValue:rackStockTakeStatus.stockTakeByUser forKey:@"stockTakeByUser"];
[rackStockTakeStatusManagedObject setValue:rackStockTakeStatus.stockTakeByUserId forKey:@"stockTakeByUserId"];
[rackStockTakeStatusManagedObject setValue:rackStockTakeStatus.beginTime forKey:@"beginTime"];
[rackStockTakeStatusManagedObject setValue:rackStockTakeStatus.percentCompleted forKey:@"percentCompleted"];
NSError *error = nil;
if (![context save:&error]) {
NSLog(@"Save Failed for Data %@! %@ %@", rackStockTakeStatus, error, [error localizedDescription]);
}
}
When you receive a response, you fill with it a non-initialized CoreData object.
if (statusCode==200)
{
for (NSDictionary *rackStockTakeStatus in returnedDict) {
RackStockTakeStatus *rackStockTakeStatusObj; // !!! MISSING INITIALIZATION HERE
rackStockTakeStatusObj.stockTakeLocId = rackStockTakeStatus[@"stockTakeLocId"];
At the time you fill it it can point on whatever piece of memory.
Common practice for such case is fetching already existing CoreData object for its ID. If no objects exist for such ID, you should create a new object. After you can fill it with the data and fetched results controller will observe changes.
if (statusCode==200)
{
for (NSDictionary *rackStockTakeStatus in returnedDict) {
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"RackStockTakeStatus"];
request.fetchLimit = 1;
request.predicate = [NSPredicate predicateWithFormat:@"stockTakeLocId = %@", rackStockTakeStatus[@"stockTakeLocId"]];
NSError *error = nil;
RackStockTakeStatus *rackStockTakeStatusObj = [[context executeFetchRequest:request error:&error] firstObject];
if (nil == rackStockTakeStatusObj)
{
rackStockTakeStatusObj = [NSEntityDescription insertNewObjectForEntityForName:@"RackStockTakeStatus" inManagedObjectContext:context]
rackStockTakeStatusObj.stockTakeLocId = rackStockTakeStatus[@"stockTakeLocId"];
}
...