iOS - passing a null managed object - - core-data

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.

Related

Managed object created in child context not reflected in main thread

I have a moc (self.managedObjectContext) which was created with NSMainQueueConcurrencyType.
Now, for a method invoked this way -
ManagedObjectType1 *obj1 = [self createAnObject];
With the implementation for createAnObject being -
- (ManagedObjectType1 *) createAnObject {
NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
childContext.parentContext = self.managedObjectContext;
ManagedObjectType1 *obj1 = //..initialize in childContext
return obj1
}
obj1 is nil after the method returns (at the place where it was invoked) and yet obj1 has data in the method implementation at the time of being returned.
What could be going wrong here. I have tried assigning childContext with NSPrivateQueueConcurrencyType but that hasn't helped either.
This worked. But is this a good way to do it.
- (ManagedObjectType1 *) createAnObject {
__block ManagedObjectType1 *obj1;
[self.managedObjectContext performBlockAndWait:^{
NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
childContext.parentContext = self.managedObjectContext;
obj1 = //..initialize in childContext
}];
return obj1
}

NSManagedObject passed to ViewController Does Reflect All Updates

In my first view controller what I'm doing is setting up a NSManagedObjectContext from a UIMangedDocument in my viewDidLoad
#property(strong, nonatomic) NSManagedObjectContext *managedObjectContext;
- (void)viewDidLoad
{
NSURL *filePath = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
filePath = [filePath URLByAppendingPathComponent:#"Locations"];
UIManagedDocument *document = [[UIManagedDocument alloc] initWithFileURL:filePath];
//Create if it doesn't exist
if (![[NSFileManager defaultManager] fileExistsAtPath:[filePath path]]) {
//Async save
[document saveToURL:filePath forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
if (success) {
self.managedObjectContext = document.managedObjectContext;
}
}];
} else if (document.documentState == UIDocumentStateClosed){
[document openWithCompletionHandler:^(BOOL success){
//Open it, don't need to refetch stuff
if (success) {
self.managedObjectContext = document.managedObjectContext;
}
}];
} else {
self.managedObjectContext = document.managedObjectContext;
}
}
Then I insert a new object via a category method on my NSMangedObject subclass
[Location createLocationWithName:#"Maui" inManagedObjectContext:self.managedObjectContext];
Which just calls this code
Location *location = [NSEntityDescription insertNewObjectForEntityForName:#"Location" inManagedObjectContext:managedObjectContext];
[managedObjectContext save:nil];
Now the problem I'm having is when I segue to a new ViewController that has a public NSManagedObjectContext property and set it to this managedObjectContext in prepareForSegue the NSFetchedResultsController in the destinationViewController doesn't pick up this change right away. After I navigate back a forth a few times it eventually sees the Location Maui I created above. Any ideas why inserting a new Object into the managedObjectContext and then passing it to another view controller doesn't reflect that change?
Any insight is greatly appreciated.
If you creation method contains the name you should at least also set this attribute (otherwise it will be lost). So second line of your creation implmentation:
location.name = name; // name is passed to the method
In order to ensure that the fetched results controller of the second view controller is updated immediately, you could set the cacheName to nil when creating the FRC. If you have lots of records and think you need the cache, you can do this in viewWillAppear:
[self.fetchedResultsController performFetch:&error];

Core Data NSMutable Set trouble

Ok I am trying to grab a NSMutable Set. Yes I have a previous post on this but this is slightly different. I have A player entity and a team entity. It is set up as a one to many relationship... On a different view controller I added players to the team. Now I am trying to get that teams players to show up on a table view... I am fetching the information as follows.
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSString *entityName = #"Team";
NSLog(#"Setting up a Fetched Results Controller for the Entity named %#", entityName);
// 2 - Request that Entity
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entityName];
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
_managedObjectContext = delegate.managedObjectContext;
// 4 - Sort it
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"players"
ascending:NO
selector:#selector(localizedCaseInsensitiveCompare:)]];
// 5 - Fetch it
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
Then on my cell for row at index path I am setting the Player object to the fetched results as follows
Player *p = [_fetchedResultsController objectAtIndexPath:indexPath];
Then, I am setting the title of the cell like so.
cell.textLabel = p.firstName;
I am getting the error
reason: 'to-many key not allowed her
I am wondering what am I doing wrong???
Figured it out! I was sorting on a one to many relationship which is a NO NO in Core Data. I switched to sort on the player object and added a predicate to find the proper players I needed.

Data Persistence with CoreData

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];

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

Resources