Core Data NSManagedObject Changes - ios

I am using a simple Master Detail scenario where the master uses a fetched results controller to populate its tableview. When a row is selected, the respective NSManagedObject is passed to the detail view controller where it is used to populate a few UILabels. I added an EditorViewController to allow some of the fields to be updated by the user. On save, I dismiss the EditorViewController and go back to the Detail view controller. The save uses the context assigned to the NSManagedObject.
What is the best way to know that the object in question changed so I can update the UI in the Detail VC? Currently, if I try to update the UILabels in the Detail VC, I don't see any of the new values for the object's properties. I know a delegate/protocol scenario would likely work but I am curious if there something already in place that I can use – a notification I can look for to update the object or if I should have a fetched results controller on the details screen? Not sure the proper path to take here.

NSFetchedResultsController is a most common solution to this problem. It listens for changes in the context and updates the table view's content accordingly. Your question is rather general, so I'll leave a link to a very good article showing how to use it properly.

You can use NSFetchedResultsController and register fetch request for that object (I think it is good solution). Alternatively you can listen for notifications like NSManagedObjectContextDidSaveNotification, but I would not recommend it.
Or you can pass an object to your EditorViewController and update properties there directly.

It's hard to say without seeing any code what you are going for, but the other common pattern for working with CoreData makes use of the Model-View-Controller(MVC) paradigm to stage info from your data store in a class variable, then your interface consumes from that class variable. You worry about saving, updating and refreshing the data between CoreData and your class variable separately from your interface consuming that data.
A typical workflow would then be:
//User does something that creates new info
//Store that info permanently in CoreData
//Refresh the class Variable by loading from CoreData (you can also skip this by updating the class variable simultaneously while saving to CoreData, but this is really sloppy and problematic. Better to save then re-fetch)
//Update your interface from the class Variable
Alternately, you could try Key Value Observing? but I'm not sure it's right for your situation.

Related

Data management with Core Data right way to do between multiple controllers

I'm having problems with the way things should work. I'm constantly having te figure out how to manage some data in some situations. Example:
I'm using core data and initialize it in my first View Controller in an array with a NSFetchRequest.
[Trip]() -> (Trip: beginDate:Date, endDate:Date, isBookmark:Bool, etc)
I have a Tableview where I only want to let the users see the trips that have a isBookmark: false, I have an other View Controller where I would only let the isBookmark: true see.
Problems I'm having now:
The moment the user creates a new trip, its default the current bookmarked trip. I can't reach all my Trip objects from the other view controller, before I was using a delegate function between my View Controllers, this is not possible anymore because I've added a TabBarController.
I'm creating a new Trip(context: context) when the user want to add a new trip. Problem now with the TabBarController is that the Core Data context that it's already showing a null element in my TableView even if I didn't call the save() context yet.
I tought maybe for the bookmarks to create a new Core Data Entity in general like "Trips with 2 atributes (notSelectedTrips, selectedTrip)" and that I move the Trips itself between these two. Is this a good idea or can I do it in an other way?
So yeah, I'm a bit stuck. If anyone could show me the way to go this would be great!
You should show a little bit more code to get a better idea where you are doing something wrong.
I assume you are using the same context for editing that you are showing the data from in your tableview, which you should not do.
You should be using a backgroundcontext for saving the changes, while a NSFetchedResultsController observes the changes on the viewContext. Once the backgroundContext saves back, the other context will show it.
Check out the documentation: https://developer.apple.com/documentation/coredata/using_core_data_in_the_background

Does Swift have a way to observe changes not just to an object, but to a reference?

I'm working on caching for an app using a subclass of NSCache. It's working well, though I'm having a hard time wrapping my mind around how to update items in the cache when necessary and propagate those changes throughout the app. Say there is a model class, Article. An instance of Article is cached, and a few view controllers in the app are observing relevant properties on this instance with KVO, so when any changes are made to the properties of this Article, the changes automatically propagate.
But say another request for that Article is made, and the cached version is now stale. A request is made to the network and a new, updated instance is serialized, so the cache and the view controllers displaying the old instance need to be updated. How can I propagate this change to the view controllers? Is there a way to observe not just a property, but observe the reference itself to get notified when it changes? In other words, do something like oldArticle = newArticle and have an observer on the oldArticle fire?
I have a few ideas of how to handle it, but none I particularly like. I could individually transfer the property values of the new Article to the old one so the relevant observers are fired - yuck. I could use an object proxy, and set up an observer on the proxy when the object that it references changes. I could use NotificationCenter or set up some delegates or something to notify the view controllers of the update. But is there a simpler way to do something as described above?
One approach to observing the whole article is to make the cache a mutable dictionary (maybe yours is already) where the keys are something essential to the article (like an id) and the values are articles. Observing view controllers can then observe both the cache's dictionary (where the key path is article id) -- to observe whole-article updates -- and any specific values they wish on the article itself.
Sticking with KVO, another approach is to give article and updateWith: (or copyFrom:) method that takes another article parameter, updating itself with the props from that parameter. The VCs in this case will keep watching the same object, and their existing update logic will work.
I would create an ArticleUpdateDelegate, and make your ArticleViewController conform. The VC then sets itself the delegate of your Article, so that whenever the article is updated, you notify the VC, which can then update its view, and do whatever else it needs.

How do I save values from the child view controllers of a UIPageViewController to use elsewhere in an app?

I have a UIPageViewController containing multiple view controllers each with questions for the user to respond to. Once these questions are answered I want to save the values somewhere locally so I can later create a PDF populated with these values. What is the best way to do this? Would I use NSUserDefaults?
It depends on how you want the data to persist. If you want the data to persist the life of the user, even if the app is deleted from the device, you would obviously need to store it away from the client such as in a database (i.e. Realm, CoreData). If you want the data to persist the life of the app itself, data that you would continue to use to shape the user's experience, you could use UserDefaults but that doesn't seem like what you're trying to do. And if you just want the data to persist the life of that instance of the app, store it in regular properties.
I suspect, for whatever reason, the third option would be your choice. If so, I would suggest creating a model object that's as an instance property of the parent view controller (that all page view controllers can reference)...
class SurveyAnswers {
var userName: String?
var userAge: Int?
var favoriteSauce: PizzaSauce?
var livesWithCats: Bool?
}
...and simply use the protocol/delegate pattern to pass data from the child to the parent which would update properties in that object (so long as they are updating the same instance of that object). Then, when you're ready to prepare the PDF file, simply ask the parent for that object, whose properties have been set by the child view controllers.
And if you're OK with it, you could use the shared AppDelegate as a way to store and retrieve this data. I would just caution against making this a habit but depending on your application as a whole, this may be a perfectly acceptable option.
General rule of thumb: set properties in destination objects to pass data forward and use delegates to pass data backward.

Using FetchedResultsController to Keep UITableView Up to Date?

I've started a small tech demo project just to get a feel for core Data. I want a table view loaded up that keeps up to date with the data stored using core data.
I think I've implemented the data model and saving the notes correctly in this. However it's not updating on the table. I'm not sure what I'm doing wrong, perhaps I haven't linked something up correctly or something?
I've checked the persistent store on my file system and the note I created is in there so I know the core data is writing to file OK.
Any help would be appreciated. I've uploaded the project as a zip to avoid pasting the whole class here. The delegate and data source of the table is set to the view controller in my storyboard too.
The project is at: https://dl.dropboxusercontent.com/u/12457690/cdtest.zip
I am afraid there are a several problems:
You have not implemented any of the NSFetchedResultsControllerDelegate methods.
How do you expect the table view to update automatically?
In your setManagedObjectContext: method, you create a fetched results controller, but don't set the delegate.
Your performFetch method is never called. Even if it were called, it would not call
performFetch: on the fetched results controller because no predicate has been set (why this restriction?).
In your deleteNote: method, you should just delete the managed object. Updating the
table view accordingly is done by the fetched results controller (if implemented correctly).
Perhaps start by implementing the NSFetchedResultsControllerDelegate methods (there is
sample code in the documentation of that protocol). Then set breakpoints in your code
and check if the delegate of the fetched results controller is set, and check if
the initial performFetch: is called.

Core Data create super entity automatically? Where and when?

I have the following datamodel to manage measurements (heart rate and skin response)
When I'm acquiring a new measurement it's going to be an entity of "MinuteStress"
Now I want to programmatically check if a corresponding day and month entity exist and if not create one automatically and add my measurement to their average.
My first question would be: Where is the right place to check for the super entities? Is it a good idea to do this in the NSManagedObjectSubclass of "MinuteStress" or is it better to do so after I create the entity in my viewcontroller?
My second question would be if there is a smart way to create super entities from a sub entity?
In theory you can do that in the awakeFromInsert method of your NSManagedObject subclass, but that's a Bad Idea (tm) because you can trigger other Core Data events... see the "special considerations" section under awakeFromInsert in the Apple Docs for more info.
You'd be better off to query for the superclasses in the view controller and create them if needed, then create the MinuteStress instance.
You might also want to write some convenience methods for creating related child objects (like -(DayStress *) createDayStress] on MonthStress for example) where you create a child object and automatically set its parent reference (and any initialization values) before returning it. It makes the code flow in the view controller much nicer IMO.

Resources