What am I doing wrong with NSManagedObjectContext dependency injection? - cocoa-touch

I am trying to use NSManagedObjectContext dependency injection as recommended by Marcus Zarra -- I'm creating an M.O.C. in my AppDelegate and passing it as a retained property to each of my view controllers.
Generally this seems to work well, but in a modal table view controller that presents data via an NSFetchedResultsController, I only see what was in the database when the app was launched. That is, if the user adds data at runtime, it gets added correctly to the database, but does not appear when the modal ViewController is opened and the NSFetchedResultsController is created (using the injected NSManagedObjectContext). However, if I close the app and restart, then open the modal view controller, I do see the data added in the previous session.
Do I have to refresh the M.O.C. in some way prior to creating the NSFetchedResultsController? I am absolutely sure that the modal view controller and the NSFetchedResultsController are being created, and the fetch is being executed, AFTER the new user data has been entered.

To start, you should log the moc in both app delegate and your view controller to confirm that the moc in both places has the same address and is therefore the same object.
If it is, then most likely you've got an issue with the FRC's cache. Set the cache to nil and/or refresh the cache and see if that resolves it.

Related

How to remove object from NSPageController arrangedObjects?

How to add/remove objects in the page controller dynamically? All examples out there set the arrangedObjects property during the awakeFromNib method once during the application lifetime.
In my application I have NSMutableArray of NSObjects that I display in the main window using NSPageController. For each NSObject I create corresponding ViewController in the delegate's - (NSViewController *) pageController:(NSPageController *) pageController
viewControllerForIdentifier:(NSString *) identifier method.
During program execution objects will get added/removed asynchronously to/from the mutable array.
At every event I set the page controller's arrangedObjects to the mutable array to account for the change - display new objects and stop showing removed ones.
When objects are added it all works fine. But when removing objects the associated views/view controllers remain in memory.
How can I "reset" the page controller so that it forgets everything about the removed objects?
Looks like the page controller keeps references (snapshots) of the view controllers of removed objects. And when adding another objects their views are messed up.
Try using a NSPageController in History Mode as described here
How do you implement NSPageController to navigate through a webView's history
and here
https://developer.apple.com/library/mac/documentation/AppKit/Reference/NSPageController_Class/
In this case you do NOT need to implement
pageController:identifierForObject:
and
pageController:viewControllerForIdentifier
and no need to set
arrangedObjects
But you NEED to call
navigateForwardToObject
each time you want to open a new page, this method makes NSPageController clear forward history and open a new page.
Also make something like adding a new view to your pageController.view with a new page in this method.
Also I made view replacement in
didTransitionToObject

Core Data separate managed object context sharing same persistent store does not see changes

I am really baffled today by what I discovered.
I thought as long as all context are using the same Core Data persistent store, any changes in one context should appear in the other context after saving the context.
E.g. In view controller A I have 1 context (context A), in view controller B, I have another context (context B). Now both context A and context B point to the same persistent store.
In context A, I fetched a managed object from the persistent store, updated a property of the managed object, then I save the changes back to the persistent store with managedObjectContext save operation.
Now I open my second view controller and perform a fetch request from the same persistent store but my second view controller does not see the updated property change, until I restart the simulator.
The really strange thing is, if it's the first time I insert a new managed object into the persistent store, controller B will see the changes but subsequent changes are not shown.
I have already fixed this problem after a long battle, I just want to know why having two separate context (both on main thread of course) sharing the same persistent store does not see the changes until simulator restart.
For those who want to know how I fixed it, in my base view controller which controller A and B both inherit from, instead of alloc-initing a new context (a hence why controller A and B have two separate contexts), I told the base view controller to reference the context in the app delegate (as a result, controller A and B now point to the same context).
Here's a diagram explaining what I am seeing:
The resulting value fetched in second view controller is the old value.
If I commit some data to the persistent store, it should become permanent and accessible ANYWHERE as long as I am fetching from that same persistent store, unless I am misunderstanding that managedObjectContext save: doesn't actually make the changes to persistent store immediately until the app results.
Zhang,
without details it's difficult to figure out the problem.
A simple suggestion is to verify if you merge changes between the two different context within your app. In other words, you need to verify the main context merges changes that come from the other one. This could simple achieved like the following.
Register for this notification where, for example, in your AppDelegate or you are creating the Core Data stack.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(contextChanged:) name:NSManagedObjectContextDidSaveNotification object:nil];
Implement the contextChanged: method to merge changes.
- (void)contextChanged:(NSNotification*)notification
{
if ([notification object] == [self managedObjectContext])
return;
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:#selector(contextChanged:) withObject:notification waitUntilDone:YES];
return;
}
[[self managedObjectContext] mergeChangesFromContextDidSaveNotification:notification];
}
For further info see Marcus Zarra tutorial.
In addition, if you use tables in combination with NSFetchedResultsController remember to implement delegate's methods. For info see NSFetchedResultsControllerDelegate class.
Hope that helps.

Magical Record, saving, and NSFetchedResultsController

Not sure if this is an issue with the way Magical Record does saves, or I'm just making an noob mistake somewhere.
I'm using an NSFetchedResultController (FRC) and UITableView to display a list of entities, when the user taps "Add" a new View Controller with an editor is pushed, a new entity is created with [MyEntity MR_createEntity]. The user can add additional entities here that are added to the main entity via a relationship. When the user taps "Save" in this View Controller the context is saved using [[NSManagedObjectContext MR_contextForCurrentThread] MR_save]
The NSFetchedResultsController appears to update, but when I tap to edit the entity none of the child entities are there. Debugging seems to show that even though the entity has been saved the FRC still has the entity with it's temporary ID.
I'm doing a naive [self.tableView reloadData] in the FRC controllerDidChangeContent delegate method.
Restarting the application loads the correct entities and the child entities are show properly in the editor view controller.
It looks like the FRC responds to the "main thread" save event, but the save is actually happening on a background thread so the FRC doesn't see it. I've checked and all "my" operations (setting up the FRC, creating and fetching entities) are all happening on the main thread context.
I tried listening for change notifications on MR_rootSavingContext and merging them with the main thread context, which sort of worked but I ended up with duplicates rows in the FRC (one was the correct "permanent" entity and one was the temporary one).
OK, I'm not sure if this is "the right way to do it" but I've found that it works correctly if I created my NSFetchedResultsController in the MR_rootSavingContext instead of the default context using the "inContext" version of MR_fetchAllSortedBy.
I guess this makes sense from the point of view that the FRC is now watching the rootSavingContext instead of one of it's children. Still, I would have thought since I'm doing all my operations on the same thread that wouldn't be an issue.
Update: The only gotcha with this approach is that if I just grab the entity using [frc objectAtIndexPath:] to give it to the editing view controller then it's no longer in the default context. Worked around this by re-fetching the entity in the default context using NSManagedObjectContext's existingObjectWithID. Still doesn't all feel quite right, but it's working for me.
Aware this is an old answer but none of the above worked for me and hopefully will help future readers
For me the problem was caused by trying to setup a purely local store using a sqlite file that had previously been used with iCloud.
Basically I tried to implement iCloud with my CoreData app, did the basic steps to set it up with an ubiquity container etc but then reverted back because of the inherent instability this seemed to cause (why is it that CoreData and iCloud STILL dont get along?!), but cocoa doesn't like you backtracking like that.
Fortunately I haven't done this in a live app so it's relatively easy to change as it will only affect development devices, but if you're moving from iCloud to a local store in a live app I think you might need to check out one of these solutions:
Migrating a Core Data Store from iCloud to local

Appropriate to delete files or close DB connection in dealloc?

I know that dealloc is called when an object's retain count reaches zero and that iVars should be released therein, but I am wondering if it is also an appropriate spot to delete temporary files or close database connections.
Specifically, I have a subclass of UIViewController that creates a database connection in -viewDidLoad and through user interaction, temporary files can be created. I would like to close the DB connection and delete the temporary files (if they exist) when the aforementioned controller gets popped of the navigation stack. Should I do so in dealloc?
My first thought was to do this DB and file clean up in -viewDidUnload, but I now know that this method is only called when a memory warning is issued by the OS. Another thought was to put it in -viewDidDisappear:animated but the issue with that approach is that the another view may go on top of the one controlled by my view controller. In that scenario, I do not want to close the DB connection or clean up the temp files.
If dealloc is not the appropriate spot (this is my gut feeling), where should this type of clean up be done? I would kind of hate to force the parent of my view controller to have to call a method in response to its child getting popped off the navigation stack.
The Apple guide to memory management says, No.
http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-SW13
In short, don't use dealloc to manage resources.
You can do so in dealloc, or if you need the files around during app lifecycle, on your app delegate's applicationWillTerminate:. YMMV.
Do not try to do this in the dealloc method of your view controller.
What would happen if the view controller gets retained somewhere you're not expecting it? The dealloc method may not get fired, and you clean up never occurs. Maybe that's not the case in the code you're writing now, but something could change in the future.
Is there any specific reason you want to tie the deletion of files and closing of the DB connection to popping off the view controller? If not, perhaps you could do this immediately your query has completed, or it may make sense to open the DB connection on startup/foreground, and then close it on termination/backgrounding.
If you must tie it to view controller being popped off the nav stack, then you could call your clean-up method from viewDidDisappear:

Get newest object added to NSFetchedResultsController?

Not even sure if this is feasible, but here's the use case:
I have a RootView tableview that uses NSFetchedResultsController to manage the list. I tap an add button, which presents the AdditionViewController. The AdditionViewController uses a separatemanagedObjectContext to create the new object. On Save, the object is passed back to the RootView, where the new object (in the new managedObjectContext) is merged into the main managedObjectContext. The AdditionViewController is then dismissed, revealing the RootView.
What I would like to do, is to push my DetailViewController with the new object loaded after the merge, so that when the AdditionViewController is dismissed, the full detail view is revealed.
How can I get the object that has just been added to the fetchedResultsController in order to pass it to the DetailViewController?
--UPDATE--
Still nothing on this. Let me try to explain what I need to do (hopefully) a bit better. If this is still confusing, ask questions. I'm having a hard time thinking of how to describe the problem.
I am fully aware how to push the Detail view underneath the modal addition view upon saving the new object. The problem is that the object I am saving is in its own fetchedReaultsController. I am merging this frc into the main fetchedResultsController, so if I try to sent the object to the detailview, I get a crash, because the object has been invalidated (due to the merge) by the time the modal addition view is dismissed, and the detailview calls viewWillAppear. That is what I am trying to get around. How can I figure out what object was just added to the main fetchedResultsController in order to send it to the detailViewController?
--UPDATE--
Adding a bounty for anyone who can tell me how to retrieve the most recently added object from a fetched results controller. Or how to retrieve a specific object from a fetched results controller without knowing it's indexPath.
Here's how I did it in an almost identical use case:
While the AdditionViewController is displayed, the user has the option of saving the item they created or cancelling out of the new item dialog. I communicated the user's choice back to the RootViewController.
If the user cancelled, remove the object you created from your context.
If the user chose to save, save the context and display the DetailViewController.
For the record, the answer was to grab the object ID of the object in the addingManagedObjectContext AFTER saving the context (since the ID changes after saving), and passing that ID to the main managedObjectContext after the merge. The full code required for this is below (if anyone has an easier way, let me know)
detailViewController.object = (customObject *)[[fetchedResultsController managedObjectContext] objectWithID:[[[[addingManagedObjectContext registeredObjects] allObjects] objectAtIndex:0] objectID]];
Thanks to frenetisch applaudierend for pointing me in the right direction.