Magical Record, saving, and NSFetchedResultsController - objective-c

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

Related

Best Practices When Using CoreBluetooth Framework

Lately I have been playing around with the bluetooth framework and grew a strong enough knowledge to start building an application. The only problem is that all the examples I found and all the practice I have made consist in putting the core bluetooth core code inside the same file as the UIView with which the user is interacting.
I would like my future application to have multiple views in which the BLE scan occurs on the background. I initially thought about creating an object with a name similar to bleDeviceFinder and pass this object through each view. However, after thinking about it I realised that if I want something to happen in the current view I need the function didDiscoverPeripheral to have direct access to the UIView objects which it is supposed to affect.
I know it is probably a stupid question, what would be the best way to do so? I was thinking maybe to set and alert and subscribe every view to that alert; is this a good solution?
A quasi singleton BTLEManager that you pass around in the app. It sends NSNotifications for events like discovery, and your ViewControllers observe these notifications. The truth (i.e. list of discovered devices) stays in BTLEManager. Once a viewController has received such a notification it asks the BTLEManager for the list of current devices and then the viewController changes your views accordingly. The Views should never talk to the BTLEManager directly.
That's how I would do it.

Core Data managedObjectContext not being updated with UI changes

I have suddenly encountered a strange problem when making updates to core data objects using UI controls bound to core data NSManagedObjects.
Symptoms are as follows:
- An OutlineView displays a list of hierarchical objects
- A Detailed view shows the data fields from the selected object, these include text fields, dates etc.
- When updates are made in the detailed view these are reflected in the OutlineView (e.g. diplayName is used in the OutlineView).
- However when I save the changes the managedObjectContext tells me there are no changes to be saved. So for some reason the UI is not letting the context know that things have changed.
- Given that the bindings are done in IB I assume that any changes in the UI would automatically be reflected in the managedObjectContext
- For some reason one of the fields seems to always result in the context recognising that changes have been made but not the others. This one field happens to be a popup list containing objects from another entity in the core data database.
If anyone has any inkling on what could be causing this - is there any way to monitor when UI changes are made, for example changing text in a text field, and whether these changes are propagated to the managedObjects.
EDIT
Found the problem - for some reason I had changed some of the properties from #dynamic to #synthesize in the objective-c classes for the core data entities. This was breaking things !
The IBOutlets need to be hooked up to the callbacks so that changes are known. They don't report changes unless you hook it up. So even though textField.text != oldText, it doesn't matter unless you take that update, save it to the entity, then see if it recognizes

State Change Monitoring

I would like to know if there are any apple Xcode APIs for monitoring state changes before I attempt to build my own. The app I work on changes out several View Controllers. Each controller can have a couple smaller custom views plus the usual check boxes, text fields etc.
The main app needs to know if a view controller or anything on it is edited from its previous state when it is pulled up before it is saved again. We only need to know if the state has changed. The caveat is this: if a user checks a checkbox , that is considered a change of state, but if the user also unchecks the check box, then the state is not changed.
I was looking at the NSUndoManager but I'm nor sure if it will work.
Any suggestions appreciated
There are a couple of approaches:
Implement a centralized "model" object. In this scenario, view controllers would just update properties of this main model object and there's little else you have to do. View controllers would then, in viewDidAppear, check the state of this model object and see if anything changed and act accordingly.
Another approach would be to implement a delegate-protocol pattern, by which the various controllers might have some delegate property that would indicate what object must be informed of data changes. This object that would be the data delegate would be defined to conform to some well defined protocol that indicate how to inform it of the changes.
If, though, you (a) have multiple objects that need to be informed of changes; and/or (b) these changes might happen asynchronously while a view is presented, you need some mechanism to do this notification. The two common approaches would be either with key-value-observing of that model object or by posting a custom notification to the NSNotificationCenter.
To advise you better, we'd need a better sense of the nature of your model object, whether updates are happening asynchronously in the background, etc.

Using NSManagedObjectContextObjectsDidChangeNotification

In many of my UIViewControllers, I update certain controls based on the state of my data. For example, I might have an edit button on a UITableViewController that should only be enabled when there is one or more items. Or perhaps I want to limit the number of items that can be added, and disable the 'add' button otherwise.
Every time I add or delete an item (or take any other action that can add/remove items), I have to remember to update any controls that might need enabling/disabling. This is trivial for the most part, but doesn't feel comfortable - there is a lot of repetition, and I have to remember to add the calls to updateControlEnabled (or whatever) whenever I add new functionality that might affect the data.
And then I noticed NSManagedObjectContextObjectsDidChangeNotification. Reading the docs, it looks like I can receive a notification whenever something changes in my managed object context. This seems ideal, but I have a few questions:
Is this an appropriate use of
NSManagedObjectContextObjectsDidChangeNotification?
Should I anticipate any performance impact if a controller
subscribes to these and parses each one to see if it needs to update
the UI? I will be checking the userInfo for every change, instead of
only those that I know I will care about.
Where should I subscribe to the notifications? My UIViewController has a
reference to the context, which helps, but I don't know where to
subscribe (loadView? viewDidLoad? init?) such that the view
controller will always have one and only one subscription.
The view controller will continue to receive and process notifications
when it's offscreen - enabling and disabling controls as the
data model is affected from elsewhere. Is this ok?
I guess I'm mostly just wondering if anyone else uses this approach and if so, what their experience is.
Q) Is this an appropriate use of NSManagedObjectContextObjectsDidChangeNotification?
A) Yes - I used it on OSX for a similar purpose.
Q) Should I anticipate any performance impact if a controller subscribes to these and parses each one to see if it needs to update the UI? I will be checking the userInfo for every change, instead of only those that I know I will care about.
A) NO - it will normally be a very small set of objects - ones that were directly changed.
Q) Where should I subscribe to the notifications? My UIViewController has a reference to the context, which helps, but I don't know where to subscribe (loadView? viewDidLoad? init?) such that the view controller will always have one and only one subscription.
A) Well, you cannot affect the UI til the view shows - so probably viewDidLoad or viewWillAppear. The problem with the later is you may get it a few times depending on push/pops, so maybe I'd do it in viewDidLoad.
Q) The view controller will continue to receive and process notifications when it's offscreen - enabling and disabling controls as the data model is affected from elsewhere. Is this ok?
A) Sure - when the view reappears all the elements will be setup correctly.
What you want to do is a classical use of that notification. Just check the thread it comes in on - if its not the mainThread then you want to make all your changes in a block posted to the mainThread.

What am I doing wrong with NSManagedObjectContext dependency injection?

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.