UAFilterableResultsController
An NSFetchedResultsController-like implementation with NSMutableArrays as the backing store.
Install / Use
/learn @unsignedapps/UAFilterableResultsControllerREADME
UAFilterableResultsController
This is an implementation of a NSFetchedResultsController-style pattern using NSMutableArray as the backing store instead of Core Data. It provides methods for manipulation and filtering of the store.
You typically set the -dataSource property on your UITableView or UICollectionView to an instance of UAFilterableResultsController and then feed it changes. Like NSFetchedResultsController, this results controller will compute the differences in the data source and notify your UITableView or UICollectionView, allowing you to animate the changes.
Overview
UAFilterableResultsController provides the following:
- A
NSMutableArraybased data source that you can manipulate. - A
UITableViewDataSourceimplementation. - A
UICollectionViewDataSourceimplementation. - Support for applying
NSPredicate-based filters on top of your data. - All changes are computed and your delegate informed (like
NSFetchedResultsController) so they can be animated.
Supported Structures
UAFilterableResultsController supports two structural types:
Two Dimension Arrays
This mirrors your typical section -> row/item setup that you use in your table or collection views. You can manipulate the objects directly, or entire sections.
Single Dimension Arrays
If you supply a flat NSArray as your data source, the UAFilterableResultsController will inform the table or collection view that the structure is a single section with the NSArray as the rows or items within that view.
Primary Keys
UAFilterableResultsController is designed to operate with data models and so works best with a primary key, but it functions perfectly fine without one.
If you supply a primary key, either through -initWithPrimaryKeyPath:delegate: or -setPrimaryKeyPath: it will attempt to perform all matching, replacing or merge operations using the value at the specified primary key path.
Say your data looks something like this:
- Employee
|-- employeeID
|-- firstName
`-- lastName
After setting your initial data source, any calls to -replaceObjects: or -mergeObjects: would look for existing objects with the same employeeID and replace those.
If no Primary Key Path is specified, UAFilterableResultsController will just use isEqual: when determining equality.
Installation
UAFilterableResultsController requires iOS6+. It has been tested against iOS6 but is only used regularly in iOS7-only production apps. It should work under OS X, but its usefulness is obviously diminished.
You can either install using CocoaPods (search for UAFilterableResultsController) or by cloning this repo into your project.
Then import:
#import <UAFilterableResultsController/UAFilterableResultsController.h>
Getting Started
Typically this is as creating an instance of the controller and assigning it as the data source to your table or collection view.
Note: UITableView and UICollectionView do not retain their data source, so you will need to keep a strong reference to your UAFilterableResultsController around, usually as a strong property on your UITableViewController or UICollectionViewController subclass.
Anyway, when using primary keys:
[self setResultsController:[[UAFilterableResultsController alloc] initWithPrimaryKeyPath:@"imageID" delegate:self]];
[self.tableView setDataSource:self.resultsController];
or when not using primary keys:
[self setResultsController:[[UAFilterableResultsController alloc] initWithDelegate:self]];
[self.collectionView setDataSource:self.resultsController];
Depending on how you load your views (to Storyboard or not to Storyboard, that is the question..) you'll want to stick these in your -awakeFromNib, -initWithStyle: or -initWithCollectionViewLayout: implementations as it is a good idea to set your dataSource before the table or collection views are loaded.
Manipulating Data
UAFilterableResultsController provides a bunch of methods for manipulating your data arrays. A few are summarised below but check the full documentation for more.
Manipulating Objects
-setData:(NSArray *)data
Sets (or replaces) the entire NSMutableArray contents with the supplied data. If your delegate is set it will be notified to reload its data, or animate the changes to the table or collection view as appropriate.
-addObject:(id)object
Appends an object to the data stack, either as the last object in the last section, or as the last object in the only section. If your delegate is set it will be notified to animate the changes to the table or collection view as appropriate.
-removeObject:(id)object
Removes the specified object from the data stack. If using the primary key path it will remove the object with the matching primary key value, otherwise the object that matches using -isEqual:. If your delegate is set it will be notified to animate the changes to the table or collection view as appropriate.
-replaceObject:(id)object
Replaces the specified object with the specified object, because that makes total sense. Actually, this is mostly used when you have a primary key path, as it will locate the object with the matching primary key value and replace it. If your delegate is set it will be notified to animate the changes to the table or collection view as appropriate.
Index Paths
It is worth noting that most of these operations have an index path equivalent, such as -removeObjectAtIndexPath: and -replaceObjectAtIndexPath:withObject:.
Merging
You can merge changes in if required. This is useful if you hit an API endpoint more than once and want to merge any changes in (say if you allow refreshing).
You can use -mergeObjects: and -mergeObjects:sortComparator: to accomplish this. If your delegate is set it will be notified to animate the changes to the table or collection view as appropriate.
If you don't supply a NSSortComparator any new objects will be appended to the data stack.
Manipulating Sections
In addition to manipulating the individual objects you can manipulate entire sections.
-addSection:, -insertSection:atIndex:, -removeSection:, -replaceSection:withSection: and -replaceSectionAtIndex:withSection: exist for this purpose. As always, if your delegate is set it will be notified to animate the changes to the table or collection view as appropriate.
Batching Updates
You can batch updates you make to the objects or structure of your data. Call -beginUpdates when you want to start making changes and -endUpdates when you are ready to commit the batch.
Batches of updates are sent to your UITableView or UICollectionView when the outermost batch has been ended. You can nest your batches.
Internally, all changes made to objects (especially large-scale merge or replace operations) are batched and submitted to be animated at the same time.
Finding Objects
UAFilterableResultsController also supports searching the data stack for objects. You can use -objectAtIndexPath: or -objectWithPrimaryKey: for locating known objects, or -indexPathOfObject: or -indexPathOfObjectWithPrimaryKey: for finding the location of the objects within the data stack.
You can pull the entire data stack with -data, or all objects within the stack using -allObjects.
Filtering
Filtering is a big part of UAFilterableResultsController, hence the name. You can easily supply a number of NSPredicate-based filters and have them automatically applied over the top of your data stack before it is presented to your table or collection view.
UAFilter
A UAFilter object is a simple wrapper around an NSPredicate that allows for naming and grouping. Only one UAFilter object belonging to the same group will be applied, any new filters with the same group will replace the existing one.
You can create a UAFilter like so:
UAFilter *filter = [UAFilter filterWithTitle:@"Micro instances"
group:@"Instance Types"
predicate:[NSPredicate predicateWithFormat:@"instanceType BEGINSWITH[c] \"t1\""]];
[self.resultsController addFilter:filter];
The filter will be applied to the data stack and your delegate is informed any changes to the rows or items, allowing you to animate the changes as required.
Manipulating Filters
Similar to objects, you can manipulate the filters applied to the data stack using -addFilter:, -addFilters:, and -removeFilter:.
You can use -replaceFilters: to remove all of the existing filters and apply the new filters, or -clearFilters to remove all filters. -appliedFilters will return an array of the currently applied filters.
Searching
If you're using a UISearchBar and want to live-filter your results you can do so easily. Avoid using UISearchDisplayController here though as we don't always play nicely trying to work with more than one table or collection view.
Instead what you can do is hook your UISearchBar up to the UAFilterableResultsController and use its filtering:
// in your UISearchBarDelegate:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
UAFilter *filter = [UAFilter filterWithName:@"Search Results"
group:@"Search Results"
predicate:[NSPredicate predicateWithFormat:@"instanceName CONTAINS[c] %@", searchText]];
[self.resultsController addFilter:filter];
}
From there, UAFilterableResultsController will take care of replacing the existing "Search Results" filter, computing the differences between the filtered data sets and informing your delegate of the changes so you can animate them in
Related Skills
node-connect
341.8kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
84.6kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
341.8kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
84.6kCommit, push, and open a PR
