Restkit 0.20 objects are not mapped after getting json response - iphone

I'm using RestKit 0.20. I'm trying to download the json response from the local rails application and save it to core-data. I tried to follow https://github.com/RestKit/RKGist/blob/master/TUTORIAL.md and everything worked fine.
I wanted to use this many times so i made it a base class(ListController) and subclassed it to SessionListController and EventListController. The mapping is provided in AppDelegate.m.
The app has a first viewcontroller which is the root controller which always maps the responses and everything works fine. But as soon as i change the viewcontroller i will get the response and the operation stops. it does't even say if the mapping has started. I'm not sure what i have missed.
Here i'm initialising restkit
AppDelegate.m
#import "AppDelegate.h"
#import <UIKit/UIKit.h>
#implementation AppDelegate
RKEntityMapping *eventEntityMapping,*sessionEntitiyMapping;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSError *error = nil;
NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"event" ofType:#"momd"]];
NSManagedObjectModel *managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] mutableCopy];
[self setManagedObjectStore: [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel]];
// Initialize the Core Data stack
[self.managedObjectStore createPersistentStoreCoordinator];
NSPersistentStore __unused *persistentStore = [self.managedObjectStore addInMemoryPersistentStore:&error];
NSAssert(persistentStore, #"Failed to add persistent store: %#", error);
[self.managedObjectStore createManagedObjectContexts];
// Set the default store shared instance
[RKManagedObjectStore setDefaultStore:self.managedObjectStore];
NSString *url=#"http://192.168.11.11:3000";
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:url]];
objectManager.managedObjectStore = self.managedObjectStore;
[RKObjectManager setSharedManager:objectManager];
[self mapEntities];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:eventEntityMapping pathPattern:#"/api/v1/events" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:responseDescriptor];
RKResponseDescriptor *responseDescriptor2 = [RKResponseDescriptor responseDescriptorWithMapping:sessionEntitiyMapping pathPattern:#"/api/v1/sessions" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:responseDescriptor2];
// Override point for customization after application launch.
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil];
UINavigationController *navigationController = [storyboard instantiateViewControllerWithIdentifier:#"mainCenterController"];
IIViewDeckController* secondDeckController = [[IIViewDeckController alloc] initWithCenterViewController:navigationController
leftViewController:[storyboard instantiateViewControllerWithIdentifier:#"sideBarController"]];
secondDeckController.centerhiddenInteractivity=IIViewDeckCenterHiddenNotUserInteractiveWithTapToClose;
self.window.rootViewController = secondDeckController;
return YES;
}
-(void)mapEntities{
//event
eventEntityMapping = [RKEntityMapping mappingForEntityForName:#"Event" inManagedObjectStore:self.managedObjectStore];
[eventEntityMapping addAttributeMappingsFromDictionary:#{
#"id": #"event_id",
#"name": #"name",
#"description": #"desc",
#"no_of_days": #"no_of_days",
#"start_date": #"start_date",
}];
eventEntityMapping.identificationAttributes = #[ #"event_id" ];
sessionEntitiyMapping = [RKEntityMapping mappingForEntityForName:#"Session" inManagedObjectStore:self.managedObjectStore];
[sessionEntitiyMapping addAttributeMappingsFromDictionary:#{
#"name": #"name",
#"description": #"desc",
#"start_time": #"start_time",
#"duration": #"duration",
#"location_id": #"location_id",
#"event_id": #"event_id",
#"id": #"session_id",
}];
sessionEntitiyMapping.identificationAttributes = #[ #"session_id" ];
}
Base class where i'm calling getObjectsAtPath for RKManager depending on the base class.
ListController.m
#implementation ListController
- (void)viewDidLoad
{
[super viewDidLoad];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
self.managedObjectContext = appDelegate.managedObjectStore.mainQueueManagedObjectContext;
[self loadLocs];
[self.refreshControl beginRefreshing];
}
-(void)setModel:(Models)model{
if(self.model!=model){
_model=model;
self.title=[Model displayFor:self.model];
_fetchedResultsController = nil;
[self.tableView reloadData];
[self loadLocs];
[self.refreshControl beginRefreshing];
}
}
- (void)loadLocs
{
[[RKObjectManager sharedManager] getObjectsAtPath:[Model listApiFor:self.model]
parameters:nil
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
[self.refreshControl endRefreshing];
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
[self.refreshControl endRefreshing];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"An Error Has Occurred" message:[error localizedDescription] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
NSLog(#"error: %#",error);
[alertView show];
}];
}
#pragma mark - Table View
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[[self fetchedRCforTableView:tableView] sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
[self configureCell:cell atIndexPath:indexPath forTableView:tableView];
return cell;
}
#pragma mark - Fetched results controller
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:[Model entityFor:self.model] 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:[Model sortDescriptorFor:self.model] ascending:YES];
NSArray *sortDescriptors = #[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:[Model displayFor:self.model]];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
NSLog(#"_fetchedResultsController count %d",_fetchedResultsController.fetchedObjects.count);
return _fetchedResultsController;
}
#end
This is one of the base classes where i set the model name
EventViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
self.model=Eventm;
// Do any additional setup after loading the view.
}
SessionViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
self.model=Sessionm;
// Do any additional setup after loading the view.
}
Full code is at https://github.com/leohemanth/event-app
After using
RKLogConfigureByName("RestKit", RKLogLevelWarning);
RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelTrace);
RKLogConfigureByName("RestKit/Network", RKLogLevelTrace);
I can see that, i'm getting proper Http response but it is not being mapped. Which i'm not sure why?

I've just updated my Restkit package to 0.20.2. I'm not facing the problem now. It should be fixed in the update.

Related

Fetching child objects belongs to parent object & put them in tableView (core-data, relationships)

I have two tableViews, the main one adds the parent object, and when you click on a parent object in the tableView, it takes you to the child tableView, I did the first part successfully, but when it comes to fetching the "RIGHT" child objects I get confused, I way I do it is that I fetch ALL child object, the using enumeration I pick the right ones and put then into a NSSet, but that doesn't work, here's the child object table view.m:
#import "MinorGoalsTableViewController.h"
#interface MinorGoalsTableViewController ()
#end
#implementation MinorGoalsTableViewController
#synthesize selectedGoal = _selectedGoal;
#synthesize fetchedResultsController = _fetchedResultsController;
#synthesize minorGoalsSet;
// init with goal (for GTVC)
- (id) initWithGoal:(Goal *)goal {
if (self = [super init]) {
_selectedGoal = goal;
}
return self;
}
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSError *error = nil;
// 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;
NSLog(#"Minor Goals in %# are: %lu", self.selectedGoal.title , (unsigned long)[self.selectedGoal.minorGoal count]);
// self.minorGoalsSet = nil;
// initializing minorGoalsSet
self.minorGoalsSet = [[NSMutableSet alloc] init];
// performing fetch
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Error fetching all minor goals: %#", error);
abort();
}
// creating NSSet that will carry all selectedGoal minor goals
NSSet *minorGoals = self.selectedGoal.minorGoal;
// creating a loop to add minor goals in minorGoalsSet
// add existing minor goals in selected goal to minorGoalsSet
for (MinorGoal *minor in minorGoals) {
[minorGoalsSet addObject:minor];
}
NSLog(#"minor goals in set: %lu", (unsigned long) [minorGoalsSet count]);
NSLog(#"minor goals in set2: %lu", (unsigned long) [minorGoals count]);
// setting nav title to selected goal title
self.navigationItem.title = _selectedGoal.title;
// adding "add" button to nav
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(addNewMinorGoal)];
}
- (void) viewWillDisappear:(BOOL)animated {
self.selectedGoal = nil;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.selectedGoal.minorGoal count];
// id <NSFetchedResultsSectionInfo> secInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
// return [secInfo numberOfObjects];
}
- (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];
}
cell.accessoryType = UITableViewCellAccessoryNone;
// Configure the cell...
MinorGoal *minor = [self.fetchedResultsController objectAtIndexPath:indexPath];
// well see about that later
if ([minorGoalsSet containsObject:minor]) {
cell.textLabel.text = minor.title;
}
// setting cell's title to minor goal's title
// cell.textLabel.text = minor.title;
return cell;
}
pragma mark - fetched results controller method
- (NSFetchedResultsController*) fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
// creating fetch request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"MinorGoal"
inManagedObjectContext:self.selectedGoal.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"title"
ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// setting _fetchedResultsController
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.selectedGoal.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
// performing fetch
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Error fetching minors: %#", error);
}
// returning
return _fetchedResultsController;
}
pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
}
- (void) addNewMinorGoal {
// code to show UIAlertView that will add new minor goal
// creating UIAlertView
UIAlertView *addMinorGoalAlert = [[UIAlertView alloc] initWithTitle:#"Add Minor Goal" message:#"Minor Goal Title" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Save", nil];
// Adding plain textfield for minor goal title
addMinorGoalAlert.alertViewStyle = UIAlertViewStylePlainTextInput;
// showing UIAlertView
[addMinorGoalAlert show];
}
pragma mark - UIAlertView for Add New Minor Delegation
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0) {
NSLog(#"Adding Minor Goal Canceled!");
}
else
{
// creating string will carry textField value
NSString *minorTitle = [[alertView textFieldAtIndex:0]text];
// creating new minor goal
MinorGoal *newMinorGoal = [NSEntityDescription
insertNewObjectForEntityForName:#"MinorGoal"
inManagedObjectContext:self.selectedGoal.managedObjectContext];
// setting title of new minor goal
newMinorGoal.title = minorTitle;
[self.selectedGoal addMinorGoalObject:newMinorGoal];
// saving
NSError *error = nil;
if (![self.selectedGoal.managedObjectContext save:&error]) {
NSLog(#"Error saving new minor goal: %#", error);
}
NSLog(#"we save %# to %# and theres %lu in it", newMinorGoal.title, self.selectedGoal.title, (unsigned long) [self.selectedGoal.minorGoal count]);
// fetching
[self.fetchedResultsController performFetch:&error];
// reloading tableView data
[self.tableView reloadData];
}
}
#end
Just tell me the right way to fetch the right child object, and should i put them in a NSSet?
It appears that you are using a NSFetchedResultsController to get ALL MinorGoal instances, but you just want to show the child MinorGoals. If you want to use an NSFetchedResultsController then you need to add a predicate to the fetch so that it only returns the MinorGoals that are children of the selected Goal. Assuming that you have created a relationship between MinorGoal and Goal called "parentGoal":
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"self.parentGoal = %#", self.selectedGoal];
Alternatively, don't us the NSFetchedResultsController and instead use the relationship. You will need to convert the NSSet to an array and sort it as the previous answer.
Store your parent object somewhere in your child view controller, typically it should be like
.h file:
#property (nonactomic, strong) ParentObject *parentObject;
.m file
- (id)initWithObject:(ParentObject *)parent {
…
_parentObject = parent;
…
}
then fetching:
(you should have #property (nonatomic, strong) NSFecthedResultsController *fetchedResultController)
- (NSFetchedResultsController*)fetchedResultController {
if (_fetchedResultController) {
return _fetchedResultController;
}
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:NSStringFromClass([ChildObject class])];
[request setEntity:entity];
NSSortDescriptor *sortDescription = [NSSortDescriptor sortDescriptorWithKey:#"sortOrder" ascending:YES];
[request setSortDescriptors:#[sortDescription]];
request.predicate = [NSPredicate predicateWithFormat:#"(parent == %#)", self.parentObject];
NSFetchedResultsController *controller = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:[NSManagedObjectContext mainThreadContext] sectionNameKeyPath:nil cacheName:nil];
controller.delegate = self;
_fetchedResultController = controller;
return _fetchedResultController;
}
then implement method
- (void)performFetch {
NSError *error = nil;
[self.fetchedResultController performFetch:&error];
if (![self.fetchedResultController performFetch:&error]) {
[self showAlertWithError:error];
}
else {
[self.tableView reloadData];
}
}
thats it basically you will receive all child objects you need sorted and filtered by predicate
seems like I was doing it the hard way, simply create new array that will hold the child objects.
NSMutableArray *minors = self.selectedGoal.minorGoals.allObjects.
and when you obtain change or add, do it two times, once to the array, and another time with the "self.selectedGoal.managedObjectContext".

table cell showing up in wrong section in table view

DataModel.h code file
#import "DataModel.h"
#import <CoreData/CoreData.h>
#import "SettingsEntity.h"
#import "Constants.h"
#implementation DataModel
NSManagedObjectContext *managedObjectContextEntity;
NSManagedObjectContext *managedObjectContextMessage;
NSManagedObjectModel *managedObjectModel;
NSPersistentStoreCoordinator *persistentStoreCoordinator;
NSEntityDescription *theSettingsEntity;
NSEntityDescription *theMessagesEntity;
-(id) init
{
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"SOW" withExtension:#"momd"];
managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
NSError *error = nil;
NSURL *storeURL = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:[Constants SQLLiteDB]];
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:managedObjectModel];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
if (persistentStoreCoordinator != nil) {
managedObjectContextEntity = [[NSManagedObjectContext alloc] init];
[managedObjectContextEntity setPersistentStoreCoordinator:persistentStoreCoordinator];
managedObjectContextMessage = [[NSManagedObjectContext alloc] init];
[managedObjectContextMessage setPersistentStoreCoordinator:persistentStoreCoordinator];
}
theSettingsEntity = [NSEntityDescription entityForName:#"SettingsEntity" inManagedObjectContext:managedObjectContextEntity];
theMessagesEntity = [NSEntityDescription entityForName:#"MessageEntity" inManagedObjectContext:managedObjectContextMessage];
return self;
}
-(void) SaveSetting: (SettingsEntity *)setting
{
NSError *error = nil;
if (managedObjectContextEntity != nil) {
if ([managedObjectContextEntity hasChanges] && ![managedObjectContextEntity save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Confirmation" message:#"Settings were saved successfully." delegate:nil cancelButtonTitle:#"Okay" otherButtonTitles:nil, nil];
[alert show];
}
}
}
-(void) SaveMessage: (MessageEntity *)message
{
NSError *error = nil;
if (managedObjectContextMessage != nil) {
if ([managedObjectContextMessage hasChanges] && ![managedObjectContextMessage save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
-(NSArray *) GetSettingsResult
{
NSManagedObjectContext *context = managedObjectContextEntity;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:theSettingsEntity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"enteredDateTime" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
NSArray *results = [context executeFetchRequest:request error:nil];
return results;
}
-(SettingsEntity *) GetSettingsEntity
{
SettingsEntity *settingsEntity = [NSEntityDescription
insertNewObjectForEntityForName:[theSettingsEntity name]
inManagedObjectContext:managedObjectContextEntity];
return settingsEntity;
}
-(NSArray *) GetMessageResult
{
NSFetchRequest *request = [self GetMessageFetchRequest];
NSArray *results = [managedObjectContextMessage executeFetchRequest:request error:nil];
return results;
}
-(NSFetchRequest *) GetMessageFetchRequest
{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:theMessagesEntity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"sentDateTime" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
return request;
}
-(NSFetchedResultsController *) GetMessageFetchResultsController
{
NSFetchRequest *fetchRequest = [self GetMessageFetchRequest];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContextMessage sectionNameKeyPath:#"typeMessage" cacheName:nil];
return aFetchedResultsController;
}
-(MessageEntity *) GetMessageEntity
{
MessageEntity *mEntity = [NSEntityDescription
insertNewObjectForEntityForName:[theMessagesEntity name]
inManagedObjectContext:managedObjectContextMessage];
return mEntity;
}
#end
HistoryTableView code file is as follows:
#import "HistoryTableViewController.h"
#import "MessageEntity.h"
#import "DataModel.h"
#import "HistoryDetailViewController.h"
#import "Constants.h"
#interface HistoryTableViewController ()
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;
#end
#implementation HistoryTableViewController
//todo: zulfiqar review this line of code.
#synthesize fetchedResultsController = __fetchedResultsController;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)awakeFromNib
{
[super awakeFromNib];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = self.editButtonItem;
//todo: get fetched controller here.. and reuse it every where in rest of this class.
}
/* faisal code starts here */
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSFetchedResultsController *c = [self fetchedResultsController];
NSArray *titles = [c sectionIndexTitles];
NSString *title = titles[section];
if ([title isEqualToString:#"E"])
{
return [Constants Email];
}
else if([title isEqualToString:#"T"])
{
return [Constants Text];
}
else
{
return [Constants Call];
}
}
- (NSArray *)sectionIndexTitlesForTableView: (UITableView *)aTableView
{
NSFetchedResultsController *c = [self fetchedResultsController];
return [c sectionIndexTitles];
}
-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSFetchedResultsController *c = [self fetchedResultsController];
id<NSFetchedResultsSectionInfo> sectionInfo = c.sections[section];
return sectionInfo.numberOfObjects;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSFetchedResultsController *c = [self fetchedResultsController];
return c.sections.count;
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *path = self.tableView.indexPathForSelectedRow;
HistoryDetailViewController *hdvc = segue.destinationViewController;
NSFetchedResultsController *c = [self fetchedResultsController];
MessageEntity *message = (MessageEntity *)[c objectAtIndexPath:path];
hdvc.currentMessageEntity = message;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - Table view data source
/*
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSFetchedResultsController *c = [self fetchedResultsController];
id <NSFetchedResultsSectionInfo> sectionInfo = c.sections[section];
return sectionInfo.numberOfObjects;
}
*/
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSFetchedResultsController *c = [self fetchedResultsController];
NSString *CellIdentifier = [NSString stringWithFormat:#"Cell"];// %d_%d",indexPath.section,indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
MessageEntity *message = (MessageEntity *)[c objectAtIndexPath:indexPath];
cell.textLabel.text = message.typeMessage;
return cell;
/*
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"EEE. MMM. dd, yyyy HH:mm"];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
NSFetchedResultsController *c = [self fetchedResultsController];
MessageEntity *message = (MessageEntity *)[c objectAtIndexPath:indexPath];
NSString *sentDateTime = [dateFormat stringFromDate:message.sentDateTime];
NSString *contactAdd;
NSLog(message.typeMessage);
if ([message.typeMessage isEqualToString:[Constants Email]])
{
contactAdd = message.toEmailAddress;
}
else
{
NSString *unformatted = message.toPhoneNumber;
NSArray *stringComponents = [NSArray arrayWithObjects:[unformatted substringWithRange:NSMakeRange(0, 3)],
[unformatted substringWithRange:NSMakeRange(3, 3)],
[unformatted substringWithRange:NSMakeRange(6, [unformatted length]-6)], nil];
contactAdd = [NSString stringWithFormat:#"(%#) %#-%#", [stringComponents objectAtIndex:0], [stringComponents objectAtIndex:1], [stringComponents objectAtIndex:2]];
}
NSString *header = [[NSString alloc] initWithFormat:#"%#",contactAdd];
NSString *detail = [[NSString alloc] initWithFormat:#"%# on %#", message.sentName, sentDateTime];
cell.detailTextLabel.text = detail;
cell.textLabel.text = header;
return cell;
*/
}
// 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;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSFetchedResultsController *c = [self fetchedResultsController];
NSManagedObject *object = [c objectAtIndexPath:indexPath];
NSManagedObjectContext *context = self.fetchedResultsController.managedObjectContext;
[context deleteObject:object];
NSError *error = nil;
if (![context save:&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.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// The table view should not be re-orderable.
return NO;
}
- (void)tableView:(UITableView *)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView reloadData];
}
- (NSFetchedResultsController *)fetchedResultsController
{
if (__fetchedResultsController != nil) {
return __fetchedResultsController;
}
DataModel *data = [[DataModel alloc]init];
NSFetchedResultsController *aFetchedResultsController = [data GetMessageFetchResultsController];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
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.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return __fetchedResultsController;
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
switch(type) {
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
{
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
/*
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:#"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
*/
}
#end
My history table view looks as follows:
**Q. Why Email and Call are getting into wrong section?**
If you set a sectionNameKeyPath for the fetched results controller, then you must also add a first sort descriptor to the fetch request that uses the same key (or a key that generates the same relative ordering).
So for sectionNameKeyPath:#"typeMessage" you should add one sort descriptor for the key "typeMessage":
NSSortDescriptor *sort1 = [[NSSortDescriptor alloc] initWithKey:#"typeMessage" ascending:YES];
NSSortDescriptor *sort2 = [[NSSortDescriptor alloc] initWithKey:#"sentDateTime" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sort1, sort2, nil];
[request setSortDescriptors:sortDescriptors];

iOS - Managed object context crashes app when launching from local notification

I have an app that creates a local notification. When the app is closed (i.e. not running or in the background), it crashes when being launched from the notification. I've managed to figure out which line is crashing the app (stated in a comment below):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackOpaque];
// Initialise the main view
self.viewController = [[[ViewController alloc] initWithNibName:#"ViewController" bundle:nil] autorelease];
// Initialise a Navigation Controller
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:self.viewController];
// Set the ViewController as the rootViewController of the window
self.window.rootViewController = nav;
// Colour the navigation bar
nav.navigationBar.tintColor = [UIColor colorWithRed:0.07f green:0.59f blue:0.94f alpha:1];
// Set the background
if (isPhone568) {
self.window.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"santa_back5.png"]];
}
else {
self.window.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"santa_back.png"]];
}
[self.window makeKeyAndVisible];
[nav release];
self.viewController.managedObjectContext = [self managedObjectContext];
application.applicationIconBadgeNumber = 0;
// Handle launching from a notification
UILocalNotification *localNotif = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (localNotif) {
NSPredicate *predicate = [NSPredicate
predicateWithFormat:#"(dateCreated like %#)",
[localNotif.userInfo objectForKey:#"dateCreated"]];
LetterViewController *letterView = [[LetterViewController alloc] initWithNibName:#"LetterViewController" bundle:nil];
// Get the letter to pass on
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Letter" inManagedObjectContext:[self managedObjectContext]];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
NSError *error;
//
// THIS NEXT LINE IS CRASHING THE APP
//
NSArray *letters = [[self managedObjectContext] executeFetchRequest:fetchRequest error:&error];
Letter *myLetter = [letters objectAtIndex:0];
letterView.theLetter = myLetter;
//[myLetter release];
// Pass the selected object to the new view controller.
[self.viewController.navigationController pushViewController:letterView animated:YES];
[letterView release];
[fetchRequest release];
}
return YES;
}
I am using the following 3 functions to get the managed object context, persistent store coordinator and managed object model:
//Explicitly write Core Data accessors
- (NSManagedObjectContext *) managedObjectContext {
if (managedObjectContext != nil) {
return managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext = [[NSManagedObjectContext alloc] init];
[managedObjectContext setPersistentStoreCoordinator: coordinator];
}
return managedObjectContext;
}
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel != nil) {
return managedObjectModel;
}
managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];
return managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory]
stringByAppendingPathComponent: #"<Project Name>.sqlite"]];
NSError *error = nil;
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel:[self managedObjectModel]];
if(![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil URL:storeUrl options:nil error:&error]) {
/*Error for store creation should be handled in here*/
}
return persistentStoreCoordinator;
}
- (NSString *)applicationDocumentsDirectory {
return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}
Any help in solving this problem would be greatly appreciated.
Thanks in advance.
Couple of things. You appear to be using Core Data on the main thread in what you show, if so all interaction must be on this thread. You should update the code someday to use the new 'perform' API which uses blocks.
Since your localNotif code now does some heavy lifting in the launch delegate it does not return for some time and iOS may kill your app. Take that same code and put it into dispatch block posted to the main queue and it should work. Add some asserts to insure the navigation controller property exists too.
Thanks for the answer. Seems my problem was with the predicate! It doesn't like me using "like" in the where clause. I changed it to an = and it works!

Pulling certain key/value pairs from core data fetched results

:D Apologies I am brand new to this and I am trying to wrap my head around core data. I have two view controllers. the first one (called PVC) is a table view controller with two sections that holds names and emails that a user enters. They press add from that screen and it takes them to the second view controller (called PDVC) where the user adds the name and email and specifies which section the entry will fall under (trustee or recipient). When they hit done on that screen, the info gets saved into core data and it takes them back to the PVC where core data fetches that info and puts it into the table view.
What I'm trying to accomplish is when the user taps send from the PVC, I need to take that info, (each name and email entered) JSON it, and send it off to a server. I'm doing this successfully with a hack I conjured up that stores each item into the dictionary, and then takes those key/value pairs and adds them to a mutable array all from the PDVC. The problem is, all of this data needs to be as persistent as core data so it makes sense to use core data for this info instead of my hack. My question is, is that possible? I need to be able to 'extract' certain key value pairs from core data. If I can do that, I'm sure I can figure out the rest. I understand that theres a method you can use to store core data info into an array but it also comes along with other info that I can't use:
array of fetched objects: (
"<Credentials: 0xb9c6ed0> (entity: Credentials; id: 0xb9bfa90 <x-coredata://BE51A026-9D1B- 4A93-BA34-4EAC55B8B9DF/Credentials/p1> ; data: {\n category = Trustees;\n name = aef;\n settingsEmail = \"inawef#hioe.com\";\n})",
"<Credentials: 0xb9c6f40> (entity: Credentials; id: 0xb9c6ae0 <x-coredata://BE51A026-9D1B- 4A93-BA34-4EAC55B8B9DF/Credentials/p3> ; data: {\n category = Trustees;\n name = awef;\n settingsEmail = \"waef#aioc.com\";\n})",
"<Credentials: 0xb9c6fc0> (entity: Credentials; id: 0xb9c6af0 <x-coredata://BE51A026-9D1B- 4A93-BA34-4EAC55B8B9DF/Credentials/p2> ;
I just need from that info the name and settingsEmail key/value pairs. Any help would be greatly appreciated! Here's the rest of my code
PVC.h
#interface PeopleViewController : UITableViewController<NSFetchedResultsControllerDelegate>
{
NSMutableArray *listOfItems;
}
- (IBAction)doneButtonPressed:(id)sender;
- (IBAction)sendPressed:(id)sender;
#property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
#end
PVC.m
#import "PeopleViewController.h"
#import "AppDelegate.h"
#import "PeopleDetailViewController.h"
#import "Credentials.h"
#import "AlertLoad.h"
#import "Data.h"
#import "ASIFormDataRequest.h"
#interface PeopleViewController ()
#end
#implementation PeopleViewController
{
NSFetchedResultsController *fetchedResultsController;
AlertLoad *alertLoad;
Data *dataObj;
}
#synthesize managedObjectContext;
- (NSFetchedResultsController *)fetchedResultsController
{
if (fetchedResultsController == nil)
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Credentials" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor1 = [NSSortDescriptor sortDescriptorWithKey:#"category" ascending:NO];
NSSortDescriptor *sortDescriptor2 = [NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sortDescriptor1, sortDescriptor2, nil]];
[fetchRequest setFetchBatchSize:20];
fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"category"
cacheName:#"Credentials"];
fetchedResultsController.delegate = self;
}
return fetchedResultsController;
}
- (void)performFetch
{
NSError *error;
if (![self.fetchedResultsController performFetch:&error]) {
FATAL_CORE_DATA_ERROR(error);
return;
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"view did load");
dataObj = [Data dataObj];
dataObj.trusteeArray = [[NSMutableArray alloc]init];
dataObj.recipientArray = [[NSMutableArray alloc]init];
if (self.managedObjectContext == nil)
{
self.managedObjectContext = [(AppDelegate *) [[UIApplication sharedApplication] delegate] managedObjectContext];
NSLog(#"After managedObjectContext: %#", self.managedObjectContext);
}
[self performFetch];
NSLog(#"array of fetched objects: %#", [fetchedResultsController fetchedObjects]);
[self.navigationController setToolbarHidden:NO];
self.navigationItem.title = #"Swipe to delete";
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
PeopleDetailViewController *controller = segue.destinationViewController;
controller.managedObjectContext = self.managedObjectContext;
}
#pragma mark Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
//return [listOfItems count];
return [[self.fetchedResultsController sections] count];
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection: (NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo name];
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
Credentials *credentials = [self.fetchedResultsController objectAtIndexPath:indexPath];
if ([credentials.name length] > 0)
{
cell.textLabel.text = credentials.name;
}
else
{
cell.textLabel.text = #"(New Entry)";
}
if (credentials.settingsEmail != nil)
{
cell.detailTextLabel.text = credentials.settingsEmail;
}
else
{
cell.detailTextLabel.text = #"Tap to edit";
}
}
- (void)tableView:(UITableView *)tableView commitEditingStyle: (UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
Credentials *credentials = [self.fetchedResultsController objectAtIndexPath:indexPath];
[self.managedObjectContext deleteObject:credentials];
NSError *error;
if (![self.managedObjectContext save:&error]) {
FATAL_CORE_DATA_ERROR(error);
return;
}
}
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier];
}
[self configureCell:cell atIndexPath:indexPath];
// Set up the cell...
return cell;
}
-(void)infoSent
{
[alertLoad close];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Success" message:#"Your trustees and recipients have been submitted" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
[self dismissViewControllerAnimated:YES completion:nil];
}
-(void)sendDataWithString:(NSString *)string
{
NSURL *url = [NSURL URLWithString:#"http://lastwords.com.au/lastwords/"];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:dataObj.username forKey:#"author"];
[request setPostValue:dataObj.password forKey:#"password"];
[request setPostValue:string forKey:#"participants"];
[request setPostValue:#"set_participants" forKey:#"json"];
[request startSynchronous];
NSError *error = [request error];
if (error != nil)
{
[alertLoad close];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:error.localizedDescription delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
NSLog(#"error: %#", error);
}
else
{
NSString *response = [request responseString];
NSLog(#"response: %#", response);
[self performSelectorOnMainThread:#selector(infoSent) withObject:nil waitUntilDone:YES];
}
}
- (IBAction)doneButtonPressed:(id)sender
{
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)sendPressed:(id)sender
{
if ([self.tableView numberOfRowsInSection:0] > 3)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Too many trustees" message:#"You can only submit a maximum of 3 trustees." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
}
else if ([self.tableView numberOfRowsInSection:0] < 3)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Not enough trustees" message:#"You must select 3 trustees." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
}
else if ([self.tableView numberOfSections] < 2)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"No trustees or recipients" message:#"You must enter at least enter 3 trustees and at least one recipient." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
}
else
{
alertLoad = [[AlertLoad alloc]
initWithTitle:#"Submitting..."
message:#"Submitting your trustees and recipients. Please wait...\n\n\n"
delegate:self cancelButtonTitle:#"Cancel"
otherButtonTitles:nil];
alertLoad.delegate = self;
[alertLoad show];
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:dataObj.trusteeArray,#"trustees", dataObj.recipientArray, #"recipients", nil];
NSError *error;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:dictionary options:kNilOptions error:&error];
NSString* jSONString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(#"json??: %#", jSONString);
[self performSelectorInBackground:#selector(sendDataWithString:) withObject:jSONString];
}
}
#end
(took out fetch results delegate stuff to save space)
PDVC.h
#interface PeopleDetailViewController : UIViewController<UITextFieldDelegate>
#property (weak, nonatomic) IBOutlet UITextField *txtFieldName;
#property (weak, nonatomic) IBOutlet UITextField *txtFieldEmail;
#property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
#property (weak, nonatomic) IBOutlet UISegmentedControl *segment;
- (IBAction)done:(id)sender;
#end
PDVC.m
#import "PeopleDetailViewController.h"
#import "AppDelegate.h"
#import "Credentials.h"
#import "Data.h"
#interface PeopleDetailViewController ()
{
Data *dataObj;
}
#end
#implementation PeopleDetailViewController
#synthesize managedObjectContext;
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"view did load");
dataObj = [Data dataObj];
if (self.managedObjectContext == nil)
{
self.managedObjectContext = [(AppDelegate *) [[UIApplication sharedApplication] delegate] managedObjectContext];
NSLog(#"After managedObjectContext: %#", self.managedObjectContext);
}
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - textField Delegate
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
- (IBAction)done:(id)sender
{
NSString *emailRegEx = #"[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
NSPredicate *emailTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", emailRegEx];
if (self.txtFieldEmail.text.length < 1 || self.txtFieldName.text.length < 1)
{
[self dismissViewControllerAnimated:YES completion:nil];
}
else if ([emailTest evaluateWithObject:self.txtFieldEmail.text] == NO)
{
//Valid email addres
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Invalid email" message:#"Please enter a valid email address" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
}
else
{
Credentials *credentials = [NSEntityDescription insertNewObjectForEntityForName:#"Credentials" inManagedObjectContext:self.managedObjectContext];
credentials.name = self.txtFieldName.text;
credentials.settingsEmail = self.txtFieldEmail.text;
if (self.segment.selectedSegmentIndex == 0)
{
credentials.category = #"Trustees";
NSDictionary *trusteeDict = [NSDictionary dictionaryWithObjectsAndKeys:self.txtFieldName.text,#"name", self.txtFieldEmail.text,#"email", nil];
NSError *error;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:trusteeDict options:kNilOptions error:&error];
NSString* jSONString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(#"json??: %#", jSONString);
[dataObj.trusteeArray addObject:trusteeDict];
NSLog(#"trusteeArray: %#", dataObj.trusteeArray);
}
else
{
credentials.category = #"Recipients";
NSDictionary *recipientDict = [NSDictionary dictionaryWithObjectsAndKeys:self.txtFieldName.text,#"name", self.txtFieldEmail.text,#"email", nil];
NSError *error;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:recipientDict options:kNilOptions error:&error];
NSString* jSONString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(#"json??: %#", jSONString);
[dataObj.recipientArray addObject:recipientDict];
NSLog(#"recipientArray: %#", dataObj.recipientArray);
}
NSError *error;
if (![self.managedObjectContext save:&error])
{
FATAL_CORE_DATA_ERROR(error);
return;
}
[self dismissViewControllerAnimated:YES completion:nil];
}
}
#end
Your question is a bit confusing but you can fetch specific information from core data with 2 very simple approaches.
1) use a predicate in the fetch like this
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"User"
inManagedObjectContext:self.managedObjectContext];
fetchRequest.entity = entity;
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"userID = %#", userIDYouAreLookingfor];
NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
NSLog(#"%#",fetchedObjects);
2) A parent relation has its entities specified in a NSSet, you can test them to retrieve certain objects:
NSSet *picturesResult = [_thumbnailsStorage.pictures objectsPassingTest:^BOOL(id obj, BOOL *stop) {
Picture *pic = obj;
// Check for a condition here
if (someCondition) {
return YES;
}
return NO;
}];
If this is not what you asked let me know and ill see if I can help.

Why Isn't Core Data Fetching My Data?

My data for for an entity's attribute is not being fetched upon restart or not being saved before quitting (could be either case). The bottom line is, the data is not showing up in the table upon restart. I think I may need to consolidate my core data methods or move them to the app delegate file instead.
I think my core data code is all messed up, can anyone help fix it?
Let me know if you need any additional code to look at from the appdelegate.m etc.
Here is the code for my View Controller:
#import "RoutineTableViewController.h"
#import "AlertPrompt.h"
#import "Routine.h"
#import "CurlAppDelegate.h"
#implementation RoutineTableViewController
#synthesize tableView;
#synthesize eventsArray;
#synthesize managedObjectContext;
- (void)dealloc
{
[managedObjectContext release];
[eventsArray release];
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (NSManagedObjectContext *) managedObjectContext {
if (managedObjectContext != nil) {
return managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext = [[NSManagedObjectContext alloc] init];
[managedObjectContext setPersistentStoreCoordinator: coordinator];
}
return managedObjectContext;
}
-(void)addEvent
{
Routine *routine = (Routine *)[NSEntityDescription insertNewObjectForEntityForName:#"Routine" inManagedObjectContext:managedObjectContext];
CurlAppDelegate *curlAppDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [curlAppDelegate managedObjectContext];
NSManagedObject *newRoutineEntry;
newRoutineEntry = [NSEntityDescription insertNewObjectForEntityForName:#"Routine" inManagedObjectContext:context];
NSError *error = nil;
if (![managedObjectContext save:&error]) {
// Handle the error.
}
[eventsArray insertObject:routine atIndex:0];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
CurlAppDelegate *curlAppDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [curlAppDelegate managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Routine" inManagedObjectContext:context];
[request setEntity:entity];
NSError *error = nil;
NSMutableArray *mutableFetchResults = [[context executeFetchRequest:request error:&error] mutableCopy];
if (mutableFetchResults == nil) {
// Handle the error.
}
[self setEventsArray:mutableFetchResults];
[mutableFetchResults release];
[request release];
UIBarButtonItem * addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(showPrompt)];
[self.navigationItem setLeftBarButtonItem:addButton];
[addButton release];
UIBarButtonItem *editButton = [[UIBarButtonItem alloc]initWithTitle:#"Edit" style:UIBarButtonItemStyleBordered target:self action:#selector(toggleEdit)];
self.navigationItem.rightBarButtonItem = editButton;
[editButton release];
[super viewDidLoad];
}
-(void)toggleEdit
{
[self.tableView setEditing: !self.tableView.editing animated:YES];
if (self.tableView.editing)
[self.navigationItem.rightBarButtonItem setTitle:#"Done"];
else
[self.navigationItem.rightBarButtonItem setTitle:#"Edit"];
}
-(void)showPrompt
{
AlertPrompt *prompt = [AlertPrompt alloc];
prompt = [prompt initWithTitle:#"Add Workout Day" message:#"\n \n Please enter title for workout day" delegate:self cancelButtonTitle:#"Cancel" okButtonTitle:#"Add"];
[prompt show];
[prompt release];
}
- (void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (buttonIndex != [alertView cancelButtonIndex])
{
NSString *entered = [(AlertPrompt *)alertView enteredText];
if(eventsArray && entered)
{
[eventsArray addObject:entered];
[tableView reloadData];
}
}
}
- (void)viewDidUnload
{
self.eventsArray = nil;
[super viewDidUnload];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [eventsArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellEditingStyleDelete reuseIdentifier:CellIdentifier] autorelease];
cell.textLabel.text = [self.eventsArray objectAtIndex:indexPath.row];
return cell;
}
// 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;
}
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the managed object at the given index path.
NSManagedObject *eventToDelete = [eventsArray objectAtIndex:indexPath.row];
[managedObjectContext deleteObject:eventToDelete];
// Update the array and table view.
[eventsArray removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES];
// Commit the change.
NSError *error = nil;
if (![managedObjectContext save:&error]) {
// Handle the error.
}
}
}
#end
And here is the data model:
Edit:
Added PersisentStoreCoordinater Method (From App Delegate):
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (__persistentStoreCoordinator != nil)
{
return __persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"Curl.sqlite"];
NSError *error = nil;
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&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.
Typical reasons for an error here include:
* The persistent store is not accessible;
* The schema for the persistent store is incompatible with current managed object model.
Check the error message to determine what the actual problem was.
If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.
If you encounter schema incompatibility errors during development, you can reduce their frequency by:
* Simply deleting the existing store:
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]
* Performing automatic lightweight migration by passing the following dictionary as the options parameter:
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return __persistentStoreCoordinator;
}
Here's a couple of tips to fix this :
Remove all references to managedObjectContext, etc. in your RoutineViewController. Only reference the CoreData classes / properties from your AppDelegate.
Whenever you need a reference (say in your AddEvent method) reference the appropriate objects from your delegate (see code below)
When this gets more complex, you'll probably want a DataManager class that handles ALL your CoreData objects. Make it a Singleton, and you'll be able to reference it in any class. For fetching objects for display, you can then create the necessary methods in this class to retrieve these objects in a NSMutableArray
CurlAppDelegate *curlAppDelegate = [[UIApplication sharedApplication] delegate]; NSManagedObjectContext *context = [curlAppDelegate managedObjectContext];
Routine *routine = (Routine *)[NSEntityDescription insertNewObjectForEntityForName:#"Routine" inManagedObjectContext:context];
See how you're now using the context from your AppDelegate and not locally? This is important, because your app will link your xdatamodel from your Bundle into your AppDelegate on initialization.
If you're concerned about the data not being saved, or overwritten, check out your AppDelegate's - (NSPersistentStoreCoordinator *)persistentStoreCoordinator
That's where the setup happens to link to your stored database when you exit and enter the app. Here's your code (modified) in your AppDelegate that loads in the Curl.sqlite file :
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator{
if (__persistentStoreCoordinator != nil)
{
return __persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"Curl.sqlite"];
NSError *error = nil;
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return __persistentStoreCoordinator;
}
My guess would be that your are mixing up your managed object contexts. Inserting into one and attempting to save on another. Remove your Core Data associated methods in your view controller and just try going through your app delegate methods. The data will either then be saved correctly, or an error will be tossed by Core Data. Make sure to inspect any setup/save errors in your Core Data code.
Have you attached your core data file in your Main Bundle? There are two types of Bundle in iPhone app. Main Bundle and Application Bundle. We had a scenario something like yours. We copied the database into our main bundle and write the location of the database into our code. then it worked fine. you can check on this issue also.