Filter NSArraycontroller Core Data before searching - swift

I have an NSArrayController managing contacts stored in Core Data and presented in a NSTable. The table provides a search field to the user to look for contacts by name. Everything is implemented in Swift using Cocoa bindings and works well.
Now I want to add an additional filter before the user types his search string. The filter should limit the contacts via an additional attribute like a creation date. How can this be done? Via modifying the predicate or do I need to subclass NSArrayController?
Any help would be appreciated.

Meanwhile I tried the approach mentioned by Willeke. Setting a default fetch predicate for the array controller initially with the Interface Builder and executing the following code every time the date date value changes, worked fine.
let fetchRequest = ContactArrayController.defaultFetchRequest()
fetchRequest.predicate = NSPredicate(format: "events.#max.eventDate < %#", lastDate as NSDate)
try? ContactArrayController.fetch(with: fetchRequest, merge: false)
where event is a related entity to the entity in the array controller.
However this appears to be an overhead to me, as the data needs to be fetched every time into the array controller instead of just applying a filter
Maybe there are better solutions

Related

Swift CoreData - Is it possible to pull data from a related entity in the fetchRequest?

Update: Answered my question by taking advantage of a to-one relationship going the reversed way. Would love to know if there is a way to expand the selection of the fetchRequest to include other entities.
My current fetch request in Swift grabs from the main table, lets call it mainTable, and filters it using an in-between entity, let's call it betweenTable, based on a single object in my third entity, thirdTable. This works. Here's how it's set up.
fetchRequest = FetchRequest<Screen>(entity: mainTable.entity(),
sortDescriptors: [NSSortDescriptor(keyPath: \mainTable.displayName, ascending: true)],
predicate: NSPredicate(format: "betweenTable.thirdTable CONTAINS[cd] %#", selectedThirdItem))
This fetch is based on a condition on the betweenEntity table (a to-many relationship) based on a value on the thirdTable (a to-one relationship). I want to grab a value from the betweenEntity table to use in my list (where the results from this fetch are shown) BUT when I try to index into the between table in the list my build times out. I was hoping there was a way to expand the values selected in the fetch request to include more than just the mainTable entity. For example select mainTable.value, betweenTable.value from ... where...
Can't find any information on this online and was hoping it was possible.
Thanks for any help.
More Information:
mainTable ->> betweenTable is a to-many relationship.
mainTable -> thirdTable is a to-one relationship but to-many reversed.
My main fetch request is for a list that I created as my previous version did live-update and needed to be forced.
Within the list I tried to get the value needed with a mainTable.betweenTable.first(where: ... type expression but then I couldn't build it so I hoped I could add a value in the select for the fetch request. Error: The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions
I had started looking into the NSFetchRequest.IncludesSubentities property but couldn't find anything helpful.
I ended up fixing this by switching my query to grab from the betweenTable entity and then follow the to-one relationship back to the mainTable for display purposes. This way I can display values from both tables. The predicate became a simple predicate: NSPredicate(format: "thirdTable == %#", selectedThirdItem)

CoreData: Can I perform a method on a FetchRequest results as it searches?

I have a TableView that lists very simple CoreData objects (they contain a name String and a couple of other fields).
I am using a FetchedResultsController, so my TableView currently uses 'fetchedResultsController.object(at: indexPath)' to populate the cells.
In my code, I have a function that converts the CoreData object .name property into a different value (i.e. "dog" might become "cat").
I want to add a search to my TableView, but I want it so the user can also search for the changed values (i.e. they can search for "dog" or they can search for "cat").
Is there a fetchRequest property I can use to perform a FetchResults query/custom filter function simultaneously?
There wasn't a solution to this using predicates (which can't use transient / computed values etc), so I ended up removing the FetchedResultsController, and doing my own filtering after my FetchRequests.

NSFetchedResultsController fetchRequest for one-many relationship

I would really appreciate some help with using NSFetchResultsController.
I think what I am need to do should be simple for some people, but I am a bit stuck!
I will try to explain what I am doing, or a simplified version of it.
I have 2 viewControllers, each displaying an NSTableView. I am using Core Data, with an entity Clubs. One of the viewControllers displays a list of Clubs. So for this one, I create an NSFetchResultsController, passing to it a fetchRequest which is basically NSFetchRequest<Clubs>(entityName: “Clubs”).
Each club has a list of Members, with a one-many relationship). I want to display the members using the 2nd viewController. So when I tap a club in the first table, I want the second table to show its members.
I have an NSFetchResultsController connected to the second table. I am stuck at what fetchRequest to send to it. If I use a similar one to the first, i.e. NSFetchRequest<Members>(entityName: “Members”), as expected, I end up with one table showing all of the clubs stored, and the second table showing ALL members stored, whereas I want only the members in the club selected in the first table. I hope that makes sense. So my question is, what fetchRequest should I send to the 2nd tables NSFetchResultsController?
You should add the following predicate to the fetch request underlying the FRC:
fetchRequest.predicate = NSPredicate(format:"club == %#", theClubSelectedInTheFirstViewController)

NSFetchedResultsController and child UITableViewController

Right now I have an UITableViewController that displays a set of Artist objects sorted into sections (alphabetically) using NSFetchedResultsController. When you tap an artist in that list, a second UITableViewController is pushed and displays the artist.shows objects, also sorted into sections (by date).
Right now I'm sorting that second data source (artist.shows) "manually", using NSSortDescriptor and a for() loop to determine where to have the table view sections (one section for every month).
Is that the right way to go? Would it make more sense to create a second NSFetchedResultsController to sort that data, although it would basically be fetching data I already have in artist.shows?
You should definitely use a second fetched results controller. It can share the managedObjectContext with the first, allowing to access the already faulted objects in that context.
You'll want to structure your predicate / sort descriptor for the controller appropriately, such as:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"artist.name == %#" argumentsArray:[NSArray arrayWithObject:selectedArtist.name]];
You could also scope it by the actual artist object but that should give you the idea.

NSFetchedResultsController ~ cache causing issues?

Ok here goes my setup:
Object Model:
* Category object is at the root
* Product is linked to a Category (many-to-1)
* ShoppingItem is linked to a Product (many-to-1)
Application setup:
My application has a TabBarController at the root and a tableviewcontroller at the root of each of the tabs.
The first tableviewcontroller shows all ShoppingItems
It uses a NSFetchedResultsController to retrieve the data
The sortdescriptor for this controller is set as "Product.Category.Name"
The sectionNameKeyPath is set as "Product.Category.Name"
There's no predicate set
Cache name is set for this NSFRC
This helps me group the shopping items based on its product categories into sections.
The second tableviewcontroller shows all Products
It uses a NSFRC as well to retrieve the data
Sortdescriptor -- "Category.Name"
SectionNameKeyPath -- "Category.Name"
The predicate set is "Active == 1" - to load only active products
Cache name is set for this NSFRC as well
This helps me group the products based on its categories into sections.
Scenario:
When I change the category associated with a product through a third screen, the tableview which displays products refreshes itself appropriately, by reslotting the product into a different (and correct) section
But the same isn't happening on the tableview which displays the shopping items
It is not an issue with the predicates I guess, as the problem is not about missing items, it is more about the items slotted in the wrong section.
Both the tableviewcontrollers are setup the same way to be notified by the NSFRCDelegate when changes to section and row info happen.
Any clues as to what's happening here? Is this behaviour correct?
P.S. : Am # work and not able to post code snippets. Can go home and do it, if that would help.
Caching could cause a temporary problem but if you make changes to the context, the cache should be refreshed automatically. You can test for this problem by deleting the FRC cache in the view controller's viewWillAppear method. That way when you come back to the view it starts with a fresh cache every time.
I think it more likely that you have two different managed object context. You have one context for the Product table and another shared by the Change-view and ShoppingItems table.
If the ShoppingItems table and the change-view share a context, then any alterations done in the change-view will appear automatically in the ShoppingItems table but the alterations will not appear in Shopping Items table unless you merge the context's changes.
If pretty much has to be separate context because the ShoppingItems can only get its category name by walking the relationship across the Product. If ShoppingItems shows the correct category but Product that must mean that each table is accessing different versions of each Product object.
Strange problem, to say the least. Could it be that NSFetchedResultsController doesn't get notified of changes to section when using a section name key path of more than 2 levels ?
You could try the following workaround to check this point :
Add a readonly property named "categoryName" to your ShoppingItem class, with following implementation :
- (NSString *)categoryName {
return self.Product.Category.Name;
}
Then, add the following method to your ShoppingItem implementation :
+ (NSSet *)keyPathsForValuesAffectingCategoryName {
return [NSSet setWithObjects:#"Product.Category.Name", nil];
}
This will ensure that each time the name of the category changes, the KVO system will also trigger a change notification for categoryName, thus hopefully getting the NSFRC notified of the change. Of course, use categoryName as the sectionNameKeyPath of your NSFRC.
Let me know if this works.
try using different cache names for different configuration (i.e, different sort or predicate) - it should be able to return correct data. It worked from me.