Data Persistence with CoreData - core-data

Hi everybody I have a problem using CoreData Persistence, my problem is, when I launch my application I manage to add some data (from a form within the app) to my DataBase and display them with using NSLog.
But actually I think all these data disappear when I stop the ipad emulator and re launch it after..
So i don't really know if it comes from my code or if it's because of the emulator.
I made a diagram to show you the architecture of my app and my entities:
The problem is that i'm using different viewController so i need to pass the ManagedObjectModel to each one. My form is in the newDocumentViewController, when i add somme entities i would like to access them in all the others viewController and save it to the app local storage.
Here is some code to show you a bit:
AppDelegate.m
#synthesize managedObjectContext = __managedObjectContext;
#synthesize managedObjectModel = __managedObjectModel;
#synthesize persistentStoreCoordinator = __persistentStoreCoordinator;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
UINavigationController *detailNavigationController = [[UINavigationController alloc] initWithRootViewController:detailViewController];
MasterViewController *masterViewController = [[MasterViewController alloc] initWithNibName:#"MasterViewController" bundle:nil];
UINavigationController *masterNavigationController = [[UINavigationController alloc] initWithRootViewController:masterViewController];
masterViewController.managedObjectContext = self.managedObjectContext;
detailViewController.managedObjectContext = self.managedObjectContext;
I have those properties within each masterViewController and DetailViewController (and from DetailViewController to NewDocumenViewController) to receive the objectContext
#property (nonatomic,strong) NSManagedObjectContext *managedObjectContext;
So with this i don't really know how to access my data from each controller and is the data is stored locally by doing like this:
NewDocumentController.m
-(void) addNewDocument:(NSString*)name with_niveau:(NSInteger)level{
Document *doc = [NSEntityDescription insertNewObjectForEntityForName:#"Document" inManagedObjectContext:managedObjectContext];
doc.nom=name;
doc.niveau=[NSNumber numberWithInteger:level];
}
-(void) addNewDocument_info:(NSString*)name with_createur:(NSString*)createur with_dateModif:(NSDate*)date1 with_status:(BOOL)etat{
DocumentInfo *doc_info = [NSEntityDescription insertNewObjectForEntityForName:#"DocumentInfo" inManagedObjectContext:managedObjectContext];
doc_info.nom =name;
doc_info.createur=createur;
doc_info.date_creation=[NSDate date];
doc_info.date_modification=date1;
doc_info.status= [NSNumber numberWithBool:etat];
}

You need to save your data:
NSError *error = nil;
[self.managedObjectContext save:&error];

Related

Saving array of images in core data as transformable

I want to add my imageArray into coredata as transformable but this is not storing properly.
My save button coding.
- (IBAction)saveButton:(id)sender {
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *newEntry = [NSEntityDescription insertNewObjectForEntityForName:#"FoodInfo" inManagedObjectContext:context];
NSAttributeDescription *messageType = [[NSAttributeDescription alloc] init];
[messageType setName:#"photos"];
[messageType setAttributeType:NSTransformableAttributeType];
[imagesForShow addObject:messageType];
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Unable to save context for class" );
} else {
NSLog(#"saved all records!");
[context save:nil];
}
//[newEntry setValue:imagesForShow forKey:#"images"];
}
Here 'imagesForShow' is my array of images.
When iam going to fetch this image array , this showing nil
- (void)viewDidLoad {
[super viewDidLoad];
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:#"FoodInfo"];
[request setReturnsObjectsAsFaults:NO];
arrayForPhotos = [[NSMutableArray alloc]initWithArray:[context executeFetchRequest:request error:nil]];
// Do any additional setup after loading the view.
}
What I am doing wrong with this code. Thanks.
In your save code:
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *newEntry = [NSEntityDescription
insertNewObjectForEntityForName:#"FoodInfo"
inManagedObjectContext:context];
NSAttributeDescription *messageType = [[NSAttributeDescription alloc] init];
[messageType setName:#"photos"];
[messageType setAttributeType:NSTransformableAttributeType];
[imagesForShow addObject:messageType];
I can't even figure out what this is supposed to do. It's completely wrong. You should never be allocating an instance of NSAttributeDescription unless you are constructing a Core Data model on the fly-- which you are not doing and which almost nobody ever does. Creating the new entry is OK. The rest, I don't know. You said that imagesForShow is your array of images, so I don't know why you're also adding an attribute description to the array.
In a more general sense, if newEntry has a transformable attribute named photos and imagesForShow is an NSArray of UIImage objects, then you could do this:
[newEntry setValue:imagesForShow forKey:#"photos"];
This is similar to a line that you have commented out, though it's not clear why it's commented out.
But whatever you do get rid of the code creating the NSAttributeDescription.

iOS - passing a null managed object -

I've read a lot of similar posts but after two days, I thought I should ask my own question.
I have a separate CoreData Controller. This passes the entity object fine from AppDelegate to the RootViewController. It does not pass it to a specific (Category) view controller, and I cant figure out why.
The code in App Delegate where I try to pass the object is this:
rootViewController.managedObjectContext = self.coreDataController.mainThreadContext;
categoryListViewController.managedObjectContext = self.coreDataController.mainThreadContext;
NSLog(#"AD/core data controller is %#", coreDataController.mainThreadContext);
NSLog(#"AD- rootVC is %#", rootViewController.managedObjectContext);
NSLog(#"AD/category list is %#", categoryListViewController.managedObjectContext);
and the logs show that the core data controller and the root vc get populated, but the Category vc doesn't.
2012-12-02 14:28:33.187 [50351:907] AD/coredatacontroller moc is <NSManagedObjectContext: 0x21065160>
2012-12-02 14:28:33.188 [50351:907] AD/categorycontroller moc is (null)
2012-12-02 14:28:33.190 [50351:907] AD- rootVC moc is <NSManagedObjectContext: 0x21065160>
Any ideas why?
UPDATE
If I do as suggested by Valentin, and init the Category VC in the App Delegate, I certainly get the managed objects passed through, however, as I call the view from the Detail VC. When I do that, I get the error "Application tried to push a nil view controller on target ".
If I try to init the category VC (and load the context) in the detail VC, it does not convey, and the logs show the context to be nil.
Init the VC (in App Delegate):
categoryListViewController = [[CategoryListViewController alloc] initWithNibName:#"CategoryList-iPad" bundle:nil];
// we have loaded from our xib, so has our CoreDataController,
// so connect as its delegate and setup its persistent store
//
self.coreDataController.delegate = self;
[self.coreDataController loadPersistentStores];
UINavigationController *rootNavigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
UINavigationController *detailNavigationController = [[UINavigationController alloc] initWithRootViewController:detailViewController];
// Set up MASTER and DETAIL delegation so we can send messages between views
rootViewController.detailViewController = detailViewController;
detailViewController.rootViewController = rootViewController;
splitViewController = [[UISplitViewController alloc] init];
splitViewController.viewControllers = #[rootNavigationController, detailNavigationController];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
splitViewController.delegate = detailViewController;
rootViewController.managedObjectContext = self.coreDataController.mainThreadContext;
categoryListViewController.managedObjectContext = self.coreDataController.mainThreadContext;
NSLog(#"AD - coreDataController is %#", coreDataController.mainThreadContext);
NSLog(#"AD - rootViewController is %#", rootViewController.managedObjectContext);
NSLog(#"AD - categoryListVC is %#", categoryListViewController.managedObjectContext);
Call the view (in DetailViewController):
-(void)categoryButtonTapped {
NSLog(#"%s", __FUNCTION__);
//categoryListViewController = [[CategoryListViewController alloc] initWithNibName:#"CategoryList-iPad" bundle:nil];
//categoryListViewController.managedObjectContext = coreDataController.mainThreadContext;
//categoryListViewController.managedObjectContext = self.coreDataController.mainThreadContext;
UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController:categoryListViewController];
nc.modalPresentationStyle = UIModalPresentationFormSheet;
NSLog(#"DVC FRC is %#", self);
NSLog(#"DVC FRC/moc is %#", coreDataController.mainThreadContext);
NSLog(#"DVC FRC/self.moc is %#", self.coreDataController.mainThreadContext);
[self presentViewController:nc animated:YES completion:nil];
//[self.navigationController pushViewController:categoryListViewController animated:YES];
}
Most probably your categoryListViewController is nil as well. Try to see if it gets alloc'ed/initialised correctly.

ios5 core data: nsfetchresultcontroller refresh uitable

i'm working on an app with core data with storyboard. the app has uitabbarcontroller as rootview. i have created entity and generated the classes. each tab has it own uinavigation controller. the view in the tab 1 just saves some data in the database from uilabels. and it works fine and data is in the database.
the view in tab 2 displays the data from the database in uitableview. the data is only shown when i kill the app and restart it. so the ui table doesnt get refreshed.
first method: i have passed the managedobject context from the app delegate to the both views. so ui table doesnt get refreshed till kill and restart.
second method: i (mis)used the app delegate, but still the same result.
MyApplicationDelegate *appDelegate = (MyApplicationDelegate *)[[UIApplication sharedApplication] delegate];
how can one achieve that one view only adds data to core data(which it does right now) and the second view get notified of changes and display it in uitableview?
edit
-(NSFetchedResultsController *) fetchedResultsController
{
if (__fetchedResultsController != nil) {
return __fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Favis" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"chapterid" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
[NSFetchedResultsController deleteCacheWithName:#"Master"];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Master"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return __fetchedResultsController;
}
it is the code when you use the core data template. only tweaked to work with my app. i have it in my both viewcontroller.
edit 2
i have implemented nsfetchedresultcontroller in my uitableview controller.
the manged object returns the exact number of data in the database, but ui table doesnt get refreshed. i also did [self.tableview reloaddata] but no luck
In the viewController with the UITableVIew implement the methods for the NSFetchedResultsControllerDelegate. The documentation has the full implementation of those methods.
And then make your viewController the delegate of the NSFetchedResultsController fetchedResultsController.delegate = self;
There should be some thing in 2 tab as to notify as data changed in database update the new data, Is there any? If
NSManagedObjectContext, NSFetchedResultsController in 2 tab by saying
Implement NSFetchedResultsController delegation methods.
in appdelegate
secTab.managedObjectContext = self.managedObjectContext;
Surely it works now

A Core-Data Relashionship not working after stopping the app

I have had one issue with Core Data and Relationship. Since this has kept me without a solution for a while and has at the same time been easy to locate I have made a tiny sample application to reproduce the problem.
Under XCode I created a barebone Window-based application, checking "Use Core Data for storage".
I called this application "CDR" for Core-Data-Relationship.
I then added a subclass of UIViewController called CDR_ViewController; as I usually do.
Here is the relevant code that I added :
First in CDR_ViewController.h :
#import <UIKit/UIKit.h>
#import "AppDelegate_Shared.h"
#interface CDR_ViewController : UIViewController {
UILabel *cdrLabel;
NSManagedObject *currentItem;
}
#property (nonatomic, retain) IBOutlet UILabel *cdrLabel;
-(IBAction) handleButtonClick:(id)sender;
#end
Then in CDR_ViewController.m the viewDidLoad method is as follow :
- (void)viewDidLoad {
[super viewDidLoad];
NSFetchRequest *request;
NSError *error;
AppDelegate_Shared *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
request=[[NSFetchRequest alloc] init];
[request setEntity: [NSEntityDescription entityForName:#"CDR_Entity" inManagedObjectContext:context]];
error=nil;
NSUInteger count = [context countForFetchRequest:request error:&error];
[request release];
if (count!=0) {
request=[[NSFetchRequest alloc] init];
[request setEntity: [NSEntityDescription entityForName:#"CDR_Entity" inManagedObjectContext:context]];
error=nil;
NSArray *objects=[context executeFetchRequest:request error:&error];
NSLog(#"Error:%#",error);
[request release];
currentItem=[objects objectAtIndex:0];
return;
}
NSManagedObject *newItemOne,*newItemTwo,*newItemThree;
request=[[NSFetchRequest alloc] init];
[request setEntity: [NSEntityDescription entityForName:#"CDR_Entity" inManagedObjectContext:context]];
newItemOne=[NSEntityDescription insertNewObjectForEntityForName:#"CDR_Entity" inManagedObjectContext:context];
newItemTwo=[NSEntityDescription insertNewObjectForEntityForName:#"CDR_Entity" inManagedObjectContext:context];
newItemThree=[NSEntityDescription insertNewObjectForEntityForName:#"CDR_Entity" inManagedObjectContext:context];
[newItemOne setValue:[NSNumber numberWithInteger:1] forKey:#"Value"];
[newItemTwo setValue:[NSNumber numberWithInteger:2] forKey:#"Value"];
[newItemThree setValue:[NSNumber numberWithInteger:3] forKey:#"Value"];
[newItemOne setValue:newItemThree forKey:#"Previous"];
[newItemOne setValue:newItemTwo forKey:#"Next"];
[newItemTwo setValue:newItemOne forKey:#"Previous"];
[newItemTwo setValue:newItemThree forKey:#"Next"];
[newItemThree setValue:newItemTwo forKey:#"Previous"];
[newItemThree setValue:newItemOne forKey:#"Next"];
error=nil;
[context save:&error];
[request release];
currentItem=newItemOne;
}
And the viewWillAppear method is as follow :
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
cdrLabel.text=[NSString stringWithFormat:#"%#",[currentItem valueForKey:#"Value"]];
}
Finally the handleButtonClick method is as follow :
-(IBAction) handleButtonClick:(id)sender
{
if (((UIButton*)sender).tag==101) {// Previous item.
currentItem=[currentItem valueForKey:#"Previous"];
} else /*(((UIButton*)sender).tag==102)*/ {// Next item.
currentItem=[currentItem valueForKey:#"Next"];
}
cdrLabel.text=[NSString stringWithFormat:#"%#",[currentItem valueForKey:#"Value"]];
}
The CDR_ViewController.xib contains one Label and two buttons.
This code works fine for start, meaning just after I compile the app and reset the contents of the iPhone simulator.
I can then cycle the contents of the label : 1,2,3,1,2,3,1,2,3 ---etc… and backward with the buttons.
As soon as I terminate the application using Command-Q. When I want to start it again, it crashes on :
currentItem=[currentItem valueForKey:#"Previous"];
or:
currentItem=[currentItem valueForKey:#"Next"];
inside the handleButtonClick method.
And it is the same when I put the app on my iPod touch.
Can anyone see in my code anything that could explain this behavior?
If you terminate an app in the simulator by stopping it, a opposed to clicking the home button in the simulator or otherwise, then the program is sent a SIGKILL message which immediately stop it running. Your application delegate methods (will terminate, will enter background etc) will not be called.
In all likelihood this will have meant that your managed object context has not been saved, so when re-running the app the object graph cannot be restored properly. In your sample app, add another button which saves the context. If you click this button, then terminate your app, does that solve the problem?

uitabbarcontroller / uitabbar in navigation based project

I have created navigation based project. and in second screen i want to add uitabbarcontroller. so can any one suggest how i do this.
i already did lot of search but no success yet. so please can you provide a simple sample of this. i already tried below discussion but i think its not a good approach.
Navigation Based Application with TabBar
Thanks
Actually this is the correct approach. The one thing that is not correct is where the controllers are allocated. This is happened in the previous controller, the one that is making the push, but should be allocated in the object that is responsible, the TabBarController.
When you implement your action to show the UITabBarController make the following code:
- (void) theAction {
SomeTabBarControllerSubClass *controller = [[SomeTabBarControllerSubClass alloc] init];
[self.navigationController pushViewController:controller animated:YES];
[controller release];
}
Then when you implement the SomeTabBarControllerSubClass class:
(.h)
#interface SomeTabBarControllerSubClass : UITabBarController {
UIViewController *first;
UIViewController *second;
}
#end
(.m)
#implementation SomeTabBarControllerSubClass
- (void) viewDidLoad {
first = [[UIViewController alloc] init]; //Or initWithNib:
second = [[UIViewController alloc] init];
first.view.backgroundColor = [UIColor greenColor] //Just example
second.view.backgroundColor = [UIColor redColor] //Just example
first.tabBarItem.image = [UIImage imageNamed:#"someImage.png"];
self.viewControllers = [NSArray arrayWithObjects:first,second,nil];
}
- (void) dealloc {
[first dealloc];
[second dealloc];
[super dealloc];
}
#end

Resources