diff --git a/Library/SJOSearchableFetchedResultsController.h b/Library/SJOSearchableFetchedResultsController.h index 98e1209..13d314f 100644 --- a/Library/SJOSearchableFetchedResultsController.h +++ b/Library/SJOSearchableFetchedResultsController.h @@ -15,12 +15,32 @@ This class provides a simpler way to replicate the often-used pattern of a searc */ @interface SJOSearchableFetchedResultsController : UITableViewController +/** + * Initialises a Core Data-backed UITableViewController with a configured with a UISearchDispalyController. + * + * @param context The managed object context to use when query Core Data. + * @param style A constant that specifies the style of table view that the controller object is to manage (UITableViewStylePlain or UITableViewStyleGrouped). + * + * @return An initialized SJOSearchableFetchedResultsController object or nil if the object couldn’t be created. + */ +- (instancetype)initWithContext:(NSManagedObjectContext *)managedObjectContext style:(UITableViewStyle)style; + /** The SJODataStore to be used when querying data. - @warning This must be set before the view is loaded. + @warning This must be set before the view is loaded unless constructed with a managed object context. */ @property (strong, nonatomic) SJODataStore *store; +/** + * The managed object context used. If the store property is set this is the `mainContext` of the SJOStore instance. + */ +@property (nonatomic, strong, readonly) NSManagedObjectContext *managedObjectContext; + +/** + * Returns YES if the user is actively searching, i.e. the search bar has begun editing. Returns NO after the user has cancelled the search. + */ +@property(nonatomic, assign, readonly) BOOL searchIsActive; + /** The UISearchDisplayController used to manage the search interface. @discussion You can customise it in your subclass to enable scope buttons, etc. @@ -77,4 +97,17 @@ Forces the fetched results controllers to be recreated, causing performFetch to */ - (void) reloadFetchedResultsControllers; +/**--------------------------------------------------------------------------------------- + * @name Methods that can overridden in subclass, but defaults are used otherwise. + * --------------------------------------------------------------------------------------- + */ +/** + * Returns the section key path string to use when constructing new NSFetchedResultsControllers. nil by default, so without overriding NSFetchedResultsControllers will have no sections. NOTE: if `searchIsActive` is YES then the return value will be ignored and nil used regardless. This is because A a section index should not be shown while searching, and B executed fetch requests take longer when sections are used. When searching this is especially noticable as a new fetch request is executed upon each key stroke during search. + * + * @param controller The SJOSearchableFetchedResultsController creating the NSFetchedResultsController for which a sectionKeyPath is needed. + * + * @return The sectionKeyPath to use in constructing a NSFetchedResultsController, or nil for no sections. + */ +- (NSString *)sectionKeyPathForSearchableFetchedResultsController:(SJOSearchableFetchedResultsController *)controller; + @end diff --git a/Library/SJOSearchableFetchedResultsController.m b/Library/SJOSearchableFetchedResultsController.m index b51d152..42d8218 100644 --- a/Library/SJOSearchableFetchedResultsController.m +++ b/Library/SJOSearchableFetchedResultsController.m @@ -14,10 +14,21 @@ @interface SJOSearchableFetchedResultsController () @property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController; @property (strong, nonatomic) NSFetchedResultsController *searchFetchedResultsController; +@property (nonatomic, strong, readwrite) NSManagedObjectContext *managedObjectContext; +@property(nonatomic, assign, readwrite) BOOL searchIsActive; @end @implementation SJOSearchableFetchedResultsController +// To override the setter the property must be synthesized +@synthesize store = _store; + +- (instancetype)initWithContext:(NSManagedObjectContext *)managedObjectContext style:(UITableViewStyle)style { + if ([super initWithStyle:style]) { + self.managedObjectContext = managedObjectContext; + } + return self; +} - (void)viewDidLoad { @@ -200,11 +211,29 @@ - (SJODataStore *)store } } +- (void)setStore:(SJODataStore *)store { + _store = store; + self.managedObjectContext = store.mainContext; +} + +- (NSString *)sectionKeyPathForSearchableFetchedResultsController:(SJOSearchableFetchedResultsController *)controller { + return nil; +} + - (NSFetchedResultsController *)newFetchedResultsControllerWithSearch:(NSString *)searchString { + NSString *sectionKeyPath; + /** + * Only use a sectionKeyPath when not searching becuase: + * - A a section index should not be shown while searching, and + * - B executed fetch requests take longer when sections are used. When searching this is especially noticable as a new fetch request is executed upon each key stroke during search. + */ + if (!self.searchIsActive) { + sectionKeyPath = [self sectionKeyPathForSearchableFetchedResultsController:self]; + } NSFetchedResultsController *fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:[self fetchRequestForSearch:searchString] - managedObjectContext:self.store.mainContext - sectionNameKeyPath:nil + managedObjectContext:self.managedObjectContext + sectionNameKeyPath:sectionKeyPath cacheName:nil]; fetchedResultsController.delegate = self; NSError *error = nil; @@ -243,6 +272,7 @@ - (NSFetchedResultsController *)searchFetchedResultsController - (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar { + self.searchIsActive = YES; [searchBar sizeToFit]; [searchBar setShowsCancelButton:YES animated:YES]; return YES; @@ -257,6 +287,7 @@ - (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar -(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar { + self.searchIsActive = NO; [self searchBarShouldEndEditing:searchBar]; }