nil is not a legal NSManagedObjectContext parameter - ios

I am getting the following error when calling core data
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+entityForName: nil is not a legal NSManagedObjectContext parameter searching for entity name 'ExcerciseInfo''
The class I am using works on another uitableview but does not work with the current one. I have pinpointed the point where the error is thrown:
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"ExcerciseInfo" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
I have looked at other similar questions but don't highlight any relavent fixes. I presume im not passing the context across as it is not the first view controller and is a second tab.
Currently in my appdelagate I am calling this function:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UITabBarController *tbc = (UITabBarController *)self.window.rootViewController;
UINavigationController *navigationController = tbc.viewControllers[0];
FBCDMasterViewController *controller = (FBCDMasterViewController *)navigationController.topViewController;
controller.managedObjectContext = self.managedObjectContext;
return YES;
}
The additional uitableview controller I am targeting is FBCDRoutineViewController
Any suggestions? I hope this is clear.

As stated by other users. I did not call the context across on the new view controller.
- (NSManagedObjectContext *)managedObjectContext
{
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}

Related

iOS - update multiple CoreData entities & contexts

I am using CoreData in my app and I have two main entities, let's say Cats and Dogs.
I have this method that I use to initialise my context:
- (NSManagedObjectContext *)managedObjectContext
{
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
It refers to this method in my App Delegate:
- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
Now, at some point, I have to update some data in my Cats entity.
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Cats"];
NSMutableArray *catsArrayData = [[context executeFetchRequest:fetchRequest error:nil] mutableCopy];
NSManagedObject *aCat = [catsArrayData objectAtIndex:i];
NSMutableArray *catsArray = [aCat valueForKey:#"cat"];
[catsArray addObject:responseData];
[aCat setValue:catsArray forKey:#"cat"];
NSError *error = nil;
[context save:&error];
Then, I want to do the same for my Dogs entity. So I use the same code, but for the dogs entity. It's in another part of my code, so I have to redefine:
NSManagedObjectContext *context = [self managedObjectContext];
But the problem is that even though I save this second context, it is actually not saved. The data isn't there. How can I resolve this issue?
You need implement your own change notification mechanism, This is good source to learn it. Multi-Context CoreData.

Attempt to Present whose view not in window hierarchy

I am having the following issue when presenting the UITableViewController.
List[12426:444700] Warning: Attempt to present <UserTableViewController: 0x7fe4ba5d8e50> on <UserViewController: 0x7fe4ba5b6db0> whose view is not in the window hierarchy!
Here's the part of my code
#implementation UserTableViewController
#synthesize users;
- (void)viewDidLoad {
// UserTableViewController
// Create Fetch Request in viewDidLoad
AppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
//Create NSManagedObjectContext instance
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"User" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
[request setEntity:entity];
[request setReturnsObjectsAsFaults:NO];
NSError *error;
NSArray *array = [[context executeFetchRequest:request error:&error]mutableCopy];
//setting users with array of fetched objects
[self setUsers:array];
NSLog(#"firstName is %#",array);
//[self presentViewController:UserTableViewController animated:YES completion:nil];
}
Really appreciate help.
You are attempting to present ([self presentViewController:animated:completion:]) UserTableViewController from within your UserViewController when your UserViewController instance is not part of the window hierarchy, e.g. on the navigation stack (has been pushed or presented itself).

Core Data One to Many Simple Example

Been trying to create a One to Many relationship with core data with little progress.
Just trying to make a simple core data example.
Entity A = Type:
typeName = String.
Entity B = Routine:
routineName = String.
Relationship: Type has many routines.
Ive got a few view controllers.
TypeViewController (Table view controller to list all the Types from Entity A.
AddTypeViewController (View controller to add a type to the entity through a typeTextField. This is pushed from a add button in the TypeViewController)
RoutineViewController (Table view controller which is pushed when a type is selected from TypeViewController. Lists the routines from Entity B associated with the type selected.
AddRoutineViewController (View controller to add Routine to entity B through routineNameTextField. This is pushed from a add button in the RoutineView Controller.
I started off with implementing just the first Entity and managed to get the First 2 view controllers working. (Was able to add types and view them on the first view controller.
Once i created the second entity and linked the relationship (also the inverse relationship too) the first part stopped working.
Q. Do i have to change the way data is fetched in view controller 1? (As in this view controller i am only fetching data from Entity A).
Q. Is it possible to be adding data separately? (adding type data from the view controller 2. and adding routine data from view controller 4.)
Q. When saving data in view controller 4...
- (IBAction)savePressed:(id)sender {
RoutinePlan *routinePlan = [[RoutinePlan alloc] initWithEntity:[NSEntityDescription entityForName:#"RoutinePlan" inManagedObjectContext:self.managedObjectContext]
insertIntoManagedObjectContext:self.managedObjectContext];
[routinePlan setValue:self.rNameTextField.text forKey:#"rName"];
**How to add this routine to the selected type**
NSError *savingError;
if (![self.managedObjectContext save:&savingError])NSLog(#"Error saving: %#", savingError);
}
Thank you for any help. If any more information is needed i can provide it...
Also does anyone know of a basic example out there of one to many relationships?
EDIT:
***Code snippet from TypeViewController.m
- (NSManagedObjectContext *)managedObjectContext{
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:#"current Type Selected"]) {
NSManagedObject *selectedRoutine = [self.routineTypeArray objectAtIndex:[[self.tableView indexPathForSelectedRow] row]];
RoutinePlanViewController *destViewController = segue.destinationViewController;
destViewController.routineTypeObject = selectedRoutine;
}}
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
// Fetch the devices from persistent data store
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Type"];
self.routineTypeArray = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
[self.tableView reloadData];}
**Code Snippet from AddTypeViewController.m
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;}
- (IBAction)savePressed:(id)sender {
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *newRoutine = [NSEntityDescription insertNewObjectForEntityForName:#"Type" inManagedObjectContext:context];
[newRoutine setValue:self.rTypeTextField.text forKey:#"rType"];
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
[self dismissViewControllerAnimated:YES completion:nil]; }
**Code Snippet from RoutineViewController.m
#synthesize routineTypeObject;
- (NSManagedObjectContext *)managedObjectContext{
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"current Type Selected"]) {
AddRoutineViewController *destViewController = segue.destinationViewController;
destViewController.routineTypeObject = routineTypeObject;
}
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// Fetch the devices from persistent data store
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Routine"];
self.routineArray = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
[self.tableView reloadData];
}
**Code Snippet from AddRoutineViewController.m
#synthesize routineTypeObject;
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
- (IBAction)savePressed:(id)sender {
Routine *routinePlan = [[Routine alloc] initWithEntity:[NSEntityDescription entityForName:#"Routine" inManagedObjectContext:self.managedObjectContext]
insertIntoManagedObjectContext:self.managedObjectContext];
[routinePlan setValue:self.rNameTextField.text forKey:#"rName"];
**Error
[Routine.Type = routineTypeObject];
NSError *savingError;
if (![self.managedObjectContext save:&savingError])NSLog(#"Error saving: %#", savingError);
}
Q. Do i have to change the way data is fetched in view controller 1? (As in this view controller i am only fetching data from Entity A).
No, you shouldn't need to (but you've given no information on how you currently do it or what any error is).
Q. Is it possible to be adding data separately? (adding type data from the view controller 2. and adding routine data from view controller 4.)
Yes, as long as you have a reference to the type that is being edited and the managedObjectContext to add to there is no limitation based on the view controller.
Q. When saving data in view controller 4...
You need to add the line that configures the relationship. The easiest way to do that is:
routinePlan.type = type;
(Where routinePlan has the relationship called type and you have the instance to be edited. Using the dot notation also requires that you have the managed object subclass).

Core Data Detail View with relationship

I have set up a 1 to many relationship on my core data entities. I am trying to show the detailview copy of the associated data. Currently I have the prepareforseague: method working with the original entity(Routines), however I am at a lose as to how to show the linked entity (RoutinesDetails).
FBCDRoutineViewController
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// Fetch the devices from persistent data store
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Routines"];
self.routines = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
[self.tableView reloadData];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"UpdateDevice"]) {
NSManagedObject *selectedDevice = [self.routines objectAtIndex:[[self.tableView indexPathForSelectedRow] row]];
FBCDRoutineViewController *destViewController = segue.destinationViewController;
destViewController.routines = selectedDevice;
}
FBCDRoutineDetailViewController
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// Fetch the devices from persistent data store
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"RoutinesDetails"];
self.routines = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
}
- (void)viewDidLoad
{
[[self navigationController] setNavigationBarHidden:NO animated:YES];
[super viewDidLoad];
// Do any additional setup after loading the view.
if (self.routines) {
[self.testLabel setText:[self.routines valueForKey:#"routinename"]];
}
}
FBCDRoutineDetailViewController
#property (strong) NSManagedObject *routines;
This is my first time with core data and I am looking at how to show the Details entity. Am I Close to getting it working? If not can I get directed at to what I should be looking at.
Any suggestions?
If I understand your problem correctly, you want to display all RoutinesDetails objects
that are related to the Routines object passed in prepareForSegue.
Then you would declare two properties in the FBCDRoutineDetailViewController:
#property (strong) NSManagedObject *routines; // the passed object
#property (strong) NSManagedObject *routineDetails; // the displayed details objects
and fetch them like this:
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"RoutinesDetails"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"routineinfo == %#", self.routines];
[fetchRequest setPredicate:predicate];
NSError *error;
self.routineDetails = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
self.routineDetails is now the data source array for the details view.
(Remark: For displaying the result set of a Core Data request in a table view,
you might also consider to use a NSFetchedResultsController.)
I think I see several problems--mostly related to the timing of these various calls.
I believe viewDidLoad is called on your detail view before the prepareForSegue is called. So your code in viewDidLoad is trying to display data about your detail item before it has been set.
Then the code in viewDidAppear looks to be overwriting the value you set in prepareForSegue, which doesn't make sense to me (although by this time the view is already displayed and it's not going to affect the label you tried to set in viewDidLoad).
Also, executeFetchRequest: returns an NSArray, not an NSManagedObject, so assigning the result of your fetch to your NSArray property is a BAD idea.

Passing context in iOS to use Core Data with Storyboard

I'm having some problems in passing context from the app delegate to the view controller.
I've found many tutorials on the internet, and all suggest to use the didFinishLaunchingWithOptions method to create the view controller, set the context property and push it.
My problem is that I want to use storyboard, and the view controller is created and pushed within it, and not in the app delegate.
I've tried to do this in my app delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//instantiate local context
NSManagedObjectContext *context = [self managedObjectContext];
if (!context)
{
// Handle the error.
NSLog(#"Error: Context is null");
}
//reference the view controller
helloCoreDataViewController1_001 *rootViewController = [helloCoreDataViewController1_001 alloc];
// Pass the managed object context to the view controller
rootViewController.managedObjectContext = context;
return YES;
}
and this in my view controller:
#implementation helloCoreDataViewController1_001
#synthesize name, address, phone, status, managedObjectContext;
//....
- (IBAction)saveContact
{
NSLog(#"name: %#",self.name.text);
NSLog(#"address: %#",self.address.text);
NSLog(#"phone: %#",self.phone.text);
//Save the new instance of the contact entity
Contact *contact = (Contact *)[NSEntityDescription insertNewObjectForEntityForName:#"Contacts" inManagedObjectContext:managedObjectContext];
[contact setContactName:[NSString stringWithFormat:#"%#",self.name.text]];
[contact setValue:self.address.text forKey:#"address"];
[contact setContactPhone:[NSString stringWithFormat:#"%#",self.phone.text]];
NSError *error = nil;
if (![managedObjectContext save:&error])
{
// Handle the error.
NSLog(#"error: %#", error.description);
self.status.text = #"Error: contact NOT saved";
}
else
self.status.text = #"Contact saved";
}
When I debug, I can see that in the app delegate, the context is populated correctly, and also the property in the view controller is ok.
But when my saveContact method is invoked, the context is empty.
Do you have any suggestions about this? How can I pass the context to the view controller with storyboard?
You can get the Storyboard's rootviewcontroller in didFinishLaunchingWithOptions by accessing
self.window.rootViewController
instead of allocing a new one.
So those two lines in your didFinishLaunchingWithOptions should look like this:
helloCoreDataViewController1_001 *rootViewController = (helloCoreDataViewController1_001 *)self.window.rootViewController;
rootViewController.managedObjectContext = context;
Instead of passing your managed obeject context to the view controller try to get it on the view controller from the appDelegate:
- (NSFetchedResultsController *)fetchedResultsController
{
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
if (managedObjectContext == nil) {
id appDelegate = (id)[[UIApplication sharedApplication] delegate];
self.managedObjectContext = [appDelegate managedObjectContext];
}... //finish your fetch request of course...
}
With me it worked beautifully

Resources