I have a view that contains a tableView. I'm using NSFetchedResultsController to display my results from Core Data.
I'd like to add a UISearchController (not a UISearchDisplayController as this one is deprecated in iOS 8) but I don't know how to link them.
Should I have only one NSFetchedResultsController or two?
I guess there is a way to fetch all the data with the NSFetchedResultsController and then just sort them according to the UISearchController am I wrong?
Thanks for the help you can give me on this.
A few thoughts...
When you create the resultsController for the searchController, you can pass to it the data that you wish to search. If you are using an NSFetchedResultsController in your main table, you could pass the fetchedObjects array. Then in response to changes to the search text, you filter the array (you could use a predicate and filteredArrayUsingPredicate to create a separate array with the search results, or you could iterate through the array to build it). The disadvantages of this route are that (unless you implement it manually) the search results will not be broken into separate sections, and the search results will not update automatically if the underlying data changes (eg. on a background thread).
I guess you could have a second NSFetchedResultsController: that would facilitate using sections, and could potentially permit the results to be updated automatically (using the delegate methods) if your data is being updated in the background, for example. But I would be nervous of the complexity that it introduces.
Another option, if you opt to apply the search in situ (i.e. specify resultsController = nil), would be to use the search criteria to update the NSFetchedResultsController itself (i.e. amending the underlying predicate and reperforming the fetch). That way your search table looks and feels exactly like the main table (including sections, if you use that) but the rows displayed obviously reduce as the search criteria become finer. This option needs care to ensure that the FRC is properly rebuilt, and might be unacceptable performance-wise, if you have a large dataset.
Related
I have been looking around everywhere online and can't seem to get a solid answer. If I have an event with multiple attributes stored in Core Data in an array and I can display all of these individual events in a tableView. Lets say one of the attributes is a date. Is there away to search for all of the events that contain a certain date and display only those in the tableView? I was thinking of using NSPredicate from what I am seeing online but I am not familiar with this. Maybe somehow find the index of the event that contains that date and only display that index? Any ideas?
Table views are designed to implement a one to one relationship with the underlying array of data. The usual approach is to use a fetchRequest to only get the data you want to display ( I personally like to have all my requests pre-defined in the data model so I can keep track of how data is accessed).
But, if your array of managed record must contain more elements than what you want to display, you should consider basing your tableView datasource responses on an intermediate array that only contains (or refers to) the objects you want to show.
For example:
let allEvents:[EventRecord] = GetAllEvents()
var filteredEvents:[EventRecord] = allEvents.filter({ $0.eventDate.isEqualToDate(dateToDisplay) })
and use filteredEvents as your underlying store for your datasource.
This will make it easy to dynamically change the filtering conditions and reload the tableview without having to go back to the database for each filter change.
I need help to think about and implement filtering of NSFetchedResultsController's fetchedObjects at the model layer.
Docs say:
Responding to Changes
In general, NSFetchedResultsController is designed to respond to changes at the model layer, by informing its
delegate when result objects change location or when sections are
modified.
NSFetchedResultsController Class Reference
For example, how could one implement search at the model layer?
I imagine a Filter entity which is somehow related to the entities being filtered, and is used in the NSFetchRequest's predicate.
Maybe I would create a Filter object, configure it with, say, a string from a search field, and then save the object. This would affect the NSFetchRequest's predicate, so the NSFetchedResultsController would get the private messages for updating its fetchedObjects and issue messages to its delegate.
I could create other Filter objects (or change existing ones) which further affects the NSFetchedResultsController's fetchedObjects, and that, when deleted, stop affecting fetchedObjects, which are then 'visible' again.
What would the model look like in Xcode's model editor?
What would the NSFetchRequest's predicate look like?
What are drawbacks with this approach?
Agree with Wain. The model layer is not the suitable mechanism to update a list when searching.
Instead, modify the predicate of the fetchRequest of the NSFetchedResultsController and re-fetch. This is a common pattern for implementing search with a UITableView.
This is unlikely to work. Your fetch would be directed at entity A and use a predicate related to entity B which holds some configuration that you update. The FRC is only monitoring changes to entity A, not entity B, so when you change the configuration it won't be picked up so no trigger for the delegate.
The only way to do it is to update the predicate and restart the FRC. The alternate being to somehow encode the information into entity A, but if you can do that you don't need the FRC as you already have all of the new search results before the FRC knows about it...
With this approach, you would need to create a Filter object for each result object for NSFetchedResultsController to pick it up. These kinds of tricks can be applied to modify user-driven sorting and sectioning but for filtering, you're better off using a predicate on NSFetchRequest which filters on actual model data.
Unless you want to do some very specific filtering which can't be done by using only the original model data. In that case it would be an alternative but you would still have to create a Filter object for each underlying data object.
I have a UITableView with data coming from NSFetchedResultsController.
Here is my tablewView:
I need to add a row "All types". It also needs to be:
Sortable with all other items
Selectable (Design is now selected)
Selecting "All types" should deselect other rows
Give something to understand that it's an "All types" row when selected
I've read Add extra row to a UITableView managed by NSFetchedResultsController and NSFetchedResultsController prepend a row or section. Given approaches makes impossible to sort data or will look so hacky and produce so much hard-maintailable code, that it will be impossible to change logic and maintain code.
Are there any other good options?
PS. I understand, that my question may sound "broad" and doesn't containt code, but I think it's very common problem.
I do not think this is a very common problem at all. I can see it seems natural to do what you are trying but lets analyse your situation: What you generally have are 2 arrays of objects which you wish to sort as a single array. Now that is quite a common situation and I believe everyone knows how to solve this issue. You need to create a single array of objects and then sort it.
The way I see it you have 3 options:
Fetch all the items, merge the 2 arrays, sort and present them. This is not a very good idea since your memory consumption can be a bit too large if there are a lot of items in the database.
Put the extra data into the database and use a fetch result controller as you would normally. This should work good but you will probably need to mark these items so they are later removed or keep it in the database but ignore them where you wish not to display them.
Create a temporary database combined with what needs to be fetched from the database and your additional data. This approach is great if your data are meant for read-only in this list (which actually seems to be the case in what you posted). Still it is best if you create some kind of link between the objects. For instance some kind of ID would be great, this way when user selects an object from the second database you simply read the ID and fetch the object from the original database.
I have one section of a table which will display an initial set of comments, and then as new comments are entered and merged into core data, it will display those as well. I could simply tack all the new comments onto the end of the current array using a standard fetch, but I would also like to handle any potential comments that were added in the meantime, and may be mixed into the sort order of the current comments.
I was trying to decide how easiest to do all of this with NSMutableOrderedSet, but really nsfrc already does everything I need, except it works with multiple sections.
Is it wise to try and shoe-horn that in there?
If you are using Core Data, NSFetchedResultsController should probably be used for all your table views which display core data entities. Even those which are not updating in the background.
In this case you do have things being merged in the background, so NSFetchedResultsController is the logical and correct choice.
I have NSFetchedResultsController like datasource of my UITableView. It displays some entities with predicate from my database. I try to find an elegant solution to insert utility row between my data rows. I don't want to create fake entity in my database cause I don't want to mix View and Model. But I need to have ability to recreate this utility row (e.g. on other application launch). Any suggestions?
It should look something like this:
You're best bet, in my opinion, is to use a section header or footer for that "utility" row. In the case of Tweetbot, they're most likely caching results locally and then merge in data when the plus button is tapped. Your table will take multiple data sets as arrays (an array of arrays) and treat each separate array as a chunk and put it into its own section.
Any way you implement you'll want to wrap your results from the database with some sort of metadata. I think you're going to have to get away from a fetched results controller, unless you use a separate instance for each chunk, keeping track of the date range for each chunk.