Skip to content

Instantly share code, notes, and snippets.

@mbbischoff
Last active August 29, 2015 14:10

Revisions

  1. mbbischoff revised this gist Nov 24, 2014. No changes.
  2. mbbischoff revised this gist Nov 24, 2014. No changes.
  3. mbbischoff revised this gist Nov 24, 2014. No changes.
  4. mbbischoff created this gist Nov 24, 2014.
    16 changes: 16 additions & 0 deletions LCKIndexedFetchedResultsController.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,16 @@
    //
    // LCKIndexedFetchedResultsController.h
    // Quotebook
    //
    // Created by Andrew Harrison on 7/26/14.
    // Copyright (c) 2014 Lickability. All rights reserved.
    //

    @import CoreData;

    /// An NSFetchedResultsController that supports proper localized indexes.
    @interface LCKIndexedFetchedResultsController : NSFetchedResultsController

    @property (nonatomic, weak) NSObject <NSFetchedResultsControllerDelegate> *delegate;

    @end
    186 changes: 186 additions & 0 deletions LCKIndexedFetchedResultsController.m
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,186 @@
    //
    // LCKIndexedFetchedResultsController.m
    // Quotebook
    //
    // Created by Andrew Harrison on 7/26/14.
    // Copyright (c) 2014 Lickability. All rights reserved.
    //

    #import "LCKIndexedFetchedResultsController.h"

    static const char LCKIndexedFetchedResultsControllerPoundSymbol = '#';

    @interface LCKIndexedFetchedResultsController () <NSFetchedResultsControllerDelegate>

    /// An internal fetched results controller that this class the delegate of.
    @property (nonatomic) NSFetchedResultsController *fetchedResultsController;

    /// A comparator used to sort the sections
    @property (nonatomic, readonly) NSComparator sectionsComparator;

    @property (nonatomic) NSArray *sectionsBeforeChange;

    @end

    @implementation LCKIndexedFetchedResultsController

    #pragma mark - NSObject

    - (void)dealloc {
    _fetchedResultsController.delegate = nil;
    }

    #pragma mark - NSFetchedResultsController

    - (instancetype)initWithFetchRequest:(NSFetchRequest *)fetchRequest managedObjectContext:(NSManagedObjectContext *)context sectionNameKeyPath:(NSString *)sectionNameKeyPath cacheName:(NSString *)name {
    self = [super init]; // Skip calling the desigated initalizer because we won’t use any of it’s functionality.

    if (self) {
    _fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:sectionNameKeyPath cacheName:name];
    _fetchedResultsController.delegate = self;
    }

    return self;
    }

    - (NSManagedObjectContext *)managedObjectContext {
    return self.fetchedResultsController.managedObjectContext;
    }

    - (NSFetchRequest *)fetchRequest {
    return self.fetchedResultsController.fetchRequest;
    }

    - (NSString *)sectionNameKeyPath {
    return self.fetchedResultsController.sectionNameKeyPath;
    }

    - (NSString *)cacheName {
    return self.fetchedResultsController.cacheName;
    }

    - (BOOL)performFetch:(NSError *__autoreleasing *)error {
    return [self.fetchedResultsController performFetch:error];
    }

    - (NSArray *)fetchedObjects {
    return self.fetchedResultsController.fetchedObjects;
    }

    #pragma mark - Modified

    - (id)objectAtIndexPath:(NSIndexPath *)indexPath {
    id <NSFetchedResultsSectionInfo> sectionInfo = [self.sections objectAtIndex:indexPath.section];

    return [sectionInfo.objects objectAtIndex:indexPath.row];
    }

    - (NSIndexPath *)indexPathForObject:(id)object {
    __block NSIndexPath *indexPath;

    [self.sections enumerateObjectsUsingBlock:^(id <NSFetchedResultsSectionInfo> sectionInfo, NSUInteger sectionIndex, BOOL *stop) {
    [[sectionInfo objects] enumerateObjectsUsingBlock:^(id rowObject, NSUInteger rowIndex, BOOL *innerStop) {
    if (object == rowObject) {
    indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex];
    *innerStop = YES;
    *stop = YES;
    }
    }];
    }];

    return indexPath;
    }

    - (NSInteger)sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)sectionIndex {
    return [[UILocalizedIndexedCollation currentCollation] sectionForSectionIndexTitleAtIndex:sectionIndex];
    }

    - (NSArray *)sectionIndexTitles {
    return [[UILocalizedIndexedCollation currentCollation] sectionIndexTitles];
    }

    - (NSArray *)sections {
    return [self.fetchedResultsController.sections sortedArrayUsingComparator:self.sectionsComparator];
    }

    #pragma mark - LCKFacadeResultsController

    - (NSComparator)sectionsComparator {
    return ^NSComparisonResult(id <NSFetchedResultsSectionInfo> sectionInfo1, id <NSFetchedResultsSectionInfo> sectionInfo2) {
    char obj1FirstChar = [[sectionInfo1 name] characterAtIndex:0];
    char obj2FirstChar = [[sectionInfo2 name] characterAtIndex:0];

    if (obj1FirstChar == LCKIndexedFetchedResultsControllerPoundSymbol && obj2FirstChar != LCKIndexedFetchedResultsControllerPoundSymbol) {
    return NSOrderedDescending;
    }
    else if (obj2FirstChar == LCKIndexedFetchedResultsControllerPoundSymbol && obj1FirstChar != LCKIndexedFetchedResultsControllerPoundSymbol) {
    return NSOrderedAscending;
    }
    else {
    return [[sectionInfo1 name] compare:[sectionInfo2 name] options:0];
    }
    };
    }

    - (NSInteger)sortedSectionIndexForUnsortedIndex:(NSInteger)unsortedIndex {
    NSArray *unsortedSections = self.fetchedResultsController.sections;
    NSArray *sortedSections = self.sections;

    id <NSFetchedResultsSectionInfo> sectionInfo = [unsortedSections safeObjectAtIndex:unsortedIndex];
    return [sortedSections indexOfObject:sectionInfo];
    }

    #pragma mark - NSFetchedResultsControllerDelegate

    - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
    if (indexPath) {
    // Construct an updated old index path based on the section’s new index after sorting.
    NSInteger sortedSectionIndex = [self sortedSectionIndexForUnsortedIndex:indexPath.section];
    indexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:sortedSectionIndex];
    }

    if (newIndexPath) {
    newIndexPath = [self indexPathForObject:anObject];
    }

    if ([self.delegate respondsToSelector:_cmd] && indexPath.section != NSNotFound) {
    [self.delegate controller:self didChangeObject:anObject atIndexPath:indexPath forChangeType:type newIndexPath:newIndexPath];
    }
    }

    - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
    if (type == NSFetchedResultsChangeDelete) {
    sectionIndex = [self.sectionsBeforeChange indexOfObject:sectionInfo];
    }
    else {
    sectionIndex = [self.sections indexOfObject:sectionInfo];
    }

    if ([self.delegate respondsToSelector:_cmd]) {
    [self.delegate controller:self didChangeSection:sectionInfo atIndex:sectionIndex forChangeType:type];
    }
    }

    - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
    self.sectionsBeforeChange = self.sections;

    if ([self.delegate respondsToSelector:_cmd]) {
    [self.delegate controllerWillChangeContent:self];
    }
    }

    - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    if ([self.delegate respondsToSelector:_cmd]) {
    [self.delegate controllerDidChangeContent:self];
    }
    }

    - (NSString *)controller:(NSFetchedResultsController *)controller sectionIndexTitleForSectionName:(NSString *)sectionName {
    if ([self.delegate respondsToSelector:_cmd]) {
    return [self.delegate controller:self sectionIndexTitleForSectionName:sectionName];
    }

    return nil;
    }

    @end