diff --git a/FirebaseUI/API/FirebaseArray.h b/FirebaseUI/API/FirebaseArray.h index cd39edd40ae..8d5e83d1996 100644 --- a/FirebaseUI/API/FirebaseArray.h +++ b/FirebaseUI/API/FirebaseArray.h @@ -61,6 +61,7 @@ * The delegate object that array changes are surfaced to. */ @property(strong, nonatomic) NSMutableArray __GENERIC(FDataSnapshot *) * snapshots; +@property(strong, nonatomic) NSMutableOrderedSet __GENERIC(id) * sectionValues; #pragma mark - #pragma mark Initializer methods @@ -79,6 +80,60 @@ */ - (instancetype)initWithQuery:(FQuery *)query; +/** + * Initializes FirebaseArray with a Firebase query (FQuery), an array of NSSortDescriptors, and an + * NSPredicate. + * Use this if you would like the array to be sorted after being received from the server, or if + * you would like more complex sorting or filtering behavior than an FQuery can provide. + * @param query A query on a Firebase reference which provides filtered data to FirebaseArray + * @param predicate The predicate by which the snapshots are filtered. If predicate is nil, the array + * reflects all results from the Firebase Query or Reference. + * @return The instance of FirebaseArray + */ +- (instancetype)initWithQuery:(FQuery *)query + predicate:(NSPredicate *)predicate; + + +/** + * Initializes FirebaseArray with a Firebase query (FQuery), an array of NSSortDescriptors, and an + * NSPredicate. + * Use this if you would like the array to be sorted after being received from the server, or if + * you would like more complex sorting or filtering behavior than an FQuery can provide. + * @param query A query on a Firebase reference which provides filtered data to FirebaseArray + * @param sortDescriptors The sort descriptors by which the array should be ordered. If the array is + * empty or nil, the array is ordered by [snapshot1.key compare:snapshot2.key] + * @param predicate The predicate by which the snapshots are filtered. If predicate is nil, the array + * reflects all results from the Firebase Query or Reference. + * @return The instance of FirebaseArray + */ +- (instancetype)initWithQuery:(FQuery *)query + sortDescriptors:(NSArray *)sortDescriptors + predicate:(NSPredicate *)predicate; + +/** + * Initializes FirebaseArray with a standard Firebase reference and an array of NSSortDescriptors. + * Use this if you would like the array to be sorted after being received from the server, or if + * you would like more complex sorting behavior. + * @param ref The Firebase reference which provides data to FirebaseArray + * @param sortDescriptors The sort descriptors by which the array should be ordered. If the array is + * empty or nil, the array is ordered by [snapshot1.key compare:snapshot2.key] + * @return The instance of FirebaseArray + */ +-(instancetype)initWithRef:(Firebase *)ref sortDescriptors:(NSArray *)sortDescriptors; + +/** + * Initializes FirebaseArray with a Firebase query (FQuery) and an array of NSSortDescriptors. + * Use this if you would like the array to be sorted after being received from the server, or if + * you would like more complex sorting behavior than an FQuery can provide. + * It is recommended that you use FQuery to filter, rather than sort, for use with this initializer. + * E.G. query only objects that have false for their hidden flag, then sort using Sort Descriptors. + * @param query A query on a Firebase reference which provides filtered data to FirebaseArray + * @param sortDescriptors The sort descriptors by which the array should be ordered. If the array is + * empty or nil, the array is ordered by [snapshot1.key compare:snapshot2.key] + * @return The instance of FirebaseArray + */ +-(instancetype)initWithQuery:(FQuery *)query sortDescriptors:(NSArray *)sortDescriptors; + #pragma mark - #pragma mark Public API methods @@ -93,27 +148,39 @@ * @param index The index of the item to retrieve * @return The object at the given index */ -- (id)objectAtIndex:(NSUInteger)index; +- (FDataSnapshot *)objectAtIndexPath:(NSIndexPath *)indexPath; /** * Returns a Firebase reference for an object at a specific index in the FirebaseArray. * @param index The index of the item to retrieve a reference for * @return A Firebase reference for the object at the given index */ -- (Firebase *)refForIndex:(NSUInteger)index; +- (Firebase *)refForIndexPath:(NSIndexPath *)indexPath; + +/** + * The sort descriptors by which the array should be ordered. If the array is empty or nil, the + * array is ordered by [snapshot1.key compare:snapshot2.key] + */ +@property (strong, nonatomic) NSArray * sortDescriptors; + +- (NSIndexPath *)indexPathOfObject:(FDataSnapshot *)snapshot; #pragma mark - #pragma mark Private API methods +- (NSIndexPath *)indexPathForKey:(NSString *)key; + /** - * Returns an index for a given object's key (that matches the object's key in the corresponding - * Firebase reference). - * @param key The key of the desired object - * @return The index of the object for which the key matches or -1 if the key is null - * @exception FirebaseArrayKeyNotFoundException Thrown when the desired key is not in the - * FirebaseArray, likely indicating that the FirebaseArray is no longer being properly synchronized - * with the Firebase database. - */ -- (NSUInteger)indexForKey:(NSString *)key; + * The predicate by which the snapshots are filtered. If predicate is nil, the array reflects all + * results from the Firebase Query or Reference. + */ +@property (strong, nonatomic) NSPredicate * predicate; + +@property (strong, nonatomic) NSString * sectionKeyPath; +@property (nonatomic) BOOL sectionsOrderedAscending; + +- (NSArray *)sectionAtIndex:(NSUInteger)sectionIndex; + +- (NSUInteger)numberOfSections; @end diff --git a/FirebaseUI/API/FirebaseArrayDelegate.h b/FirebaseUI/API/FirebaseArrayDelegate.h index c47e789e0b4..0aa81760999 100644 --- a/FirebaseUI/API/FirebaseArrayDelegate.h +++ b/FirebaseUI/API/FirebaseArrayDelegate.h @@ -44,9 +44,9 @@ * [FEventTypeChildAdded](https://www.firebase.com/docs/ios/guide/retrieving-data.html#section-event-types) * event being raised. * @param object The object added to the FirebaseArray - * @param index The index the child was added at + * @param indexPath The indexPath the child was added at */ -- (void)childAdded:(id)object atIndex:(NSUInteger)index; +- (void)childAdded:(id)obj atIndexPath:(NSIndexPath *)indexPath; /** * Delegate method which is called whenever an object is chinged in a FirebaseArray. On a @@ -54,9 +54,9 @@ * [FEventTypeChildChanged](https://www.firebase.com/docs/ios/guide/retrieving-data.html#section-event-types) * event being raised. * @param object The object that changed in the FirebaseArray - * @param index The index the child was changed at + * @param indexPath The indexPath the child was changed at */ -- (void)childChanged:(id)object atIndex:(NSUInteger)index; +- (void)childChanged:(id)obj atIndexPath:(NSIndexPath *)indexPath; /** * Delegate method which is called whenever an object is removed from a FirebaseArray. On a @@ -64,9 +64,9 @@ * [FEventTypeChildRemoved](https://www.firebase.com/docs/ios/guide/retrieving-data.html#section-event-types) * event being raised. * @param object The object removed from the FirebaseArray - * @param index The index the child was removed at + * @param indexPath The indexPath the child was removed at */ -- (void)childRemoved:(id)object atIndex:(NSUInteger)index; +- (void)childRemoved:(id)obj atIndexPath:(NSIndexPath *)indexPath; /** * Delegate method which is called whenever an object is moved within a FirebaseArray. On a @@ -74,9 +74,20 @@ * [FEventTypeChildMoved](https://www.firebase.com/docs/ios/guide/retrieving-data.html#section-event-types) * event being raised. * @param object The object that has moved locations in the FirebaseArray - * @param fromIndex The index the child is being moved from - * @param toIndex The index the child is being moved to + * @param fromIndexPath The indexPath the child is being moved from + * @param toIndexPath The indexPath the child is being moved to */ -- (void)childMoved:(id)object fromIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex; +- (void)childMoved:(id)obj + fromIndexPath:(NSIndexPath *)fromIndexPath + toIndexPath:(NSIndexPath *)toIndexPath; + +- (void)sectionsAddedAtIndexes:(NSIndexSet *)indexes; + +- (void)sectionAddedAtSectionIndex:(NSUInteger)section; + +- (void)sectionRemovedAtSectionIndex:(NSUInteger)section; + +- (void)beginUpdates; +- (void)endUpdates; @end diff --git a/FirebaseUI/API/FirebaseCollectionViewDataSource.h b/FirebaseUI/API/FirebaseCollectionViewDataSource.h index 34dde18dbd2..681f992e25f 100644 --- a/FirebaseUI/API/FirebaseCollectionViewDataSource.h +++ b/FirebaseUI/API/FirebaseCollectionViewDataSource.h @@ -45,7 +45,7 @@ * synchronized to a * Firebase reference or query. In addition to handling all Firebase child * events (added, changed, - * removed, moved), FirebaseCollectionViewDataSource handles UITableViewCell + * removed, moved), FirebaseCollectionViewDataSource handles UICollectionViewCell * creation, either with * the default UICollectionViewCell, prototype cells, custom * UICollectionViewCell subclasses, or @@ -103,7 +103,174 @@ */ @property(strong, nonatomic, __NON_NULL) void (^populateCell) (__KINDOF(UICollectionViewCell) __NON_NULL_PTR cell, - __KINDOF(NSObject) __NON_NULL_PTR object); +__KINDOF(NSObject) __NON_NULL_PTR object); + +/** + * Initialize an instance of FirebaseCollectionViewDataSource that populates + * UICollectionViewCells with + * FDataSnapshots. + * @param ref A Firebase reference to bind the datasource to + * @param sortDescriptors The sort descriptors by which the array should be ordered. + * @param identifier A string to use as a CellReuseIdentifier + * @param collectionView An instance of a UICollectionView to bind to + * @return An instance of FirebaseCollectionViewDataSource that populates + * UICollectionViewCells with + * FDataSnapshots + */ +- (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + sortDescriptors:(__NULLABLE NSArray *)sortDescriptors + cellReuseIdentifier:(__NON_NULL NSString *)identifier + view:(__NON_NULL UICollectionView *)collectionView; + +/** + * Initialize an instance of FirebaseCollectionViewDataSource that populates + * UICollectionViewCells with + * FDataSnapshots. Note that this method is used when using prototype cells, + * where the cells don't + * need to be registered in the class. + * @param ref A Firebase reference to bind the datasource to + * @param identifier A string to use as a CellReuseIdentifier + * @param collectionView An instance of a UICollectionView to bind to + * @return An instance of FirebaseCollectionViewDataSource that populates + * UICollectionViewCells with + * FDataSnapshots + */ +- (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + sortDescriptors:(__NULLABLE NSArray *)sortDescriptors + prototypeReuseIdentifier:(__NON_NULL NSString *)identifier + view:(__NON_NULL UICollectionView *)collectionView; + +/** + * Initialize an instance of FirebaseCollectionViewDataSource that populates a custom + * subclass of + * UICollectionViewCell with FDataSnapshots. + * @param ref A Firebase reference to bind the datasource to + * @param sortDescriptors The sort descriptors by which the array should be ordered. + * @param cell A subclass of UICollectionViewCell used to populate the UICollectionView, + * defaults to + * UICollectionViewCell if nil + * @param identifier A string to use as a CellReuseIdentifier + * @param collectionView An instance of a UICollectionView to bind to + * @return An instance of FirebaseCollectionViewDataSource that populates a custom + * subclass of + * UICollectionViewCell with FDataSnapshots + */ +- (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + sortDescriptors:(__NULLABLE NSArray *)sortDescriptors + cellClass:(__NULLABLE Class)cell + cellReuseIdentifier:(__NON_NULL NSString *)identifier + view:(__NON_NULL UICollectionView *)collectionView; + +/** + * Initialize an instance of FirebaseCollectionViewDataSource that populates a custom + * xib with + * FDataSnapshots. + * @param ref A Firebase reference to bind the datasource to + * @param sortDescriptors The sort descriptors by which the array should be ordered. + * @param nibName The name of a xib file to create the layout for a + * UICollectionViewCell + * @param identifier A string to use as a CellReuseIdentifier + * @param collectionView An instance of a UICollectionView to bind to + * @return An instance of FirebaseCollectionViewDataSource that populates a custom + * xib with + * FDataSnapshots + */ +- (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + sortDescriptors:(__NULLABLE NSArray *)sortDescriptors + nibNamed:(__NON_NULL NSString *)nibName + cellReuseIdentifier:(__NON_NULL NSString *)identifier + view:(__NON_NULL UICollectionView *)collectionView; + +/** + * Initialize an instance of FirebaseCollectionViewDataSource that populates + * UICollectionViewCells with a + * custom model class. + * @param ref A Firebase reference to bind the datasource to + * @param sortDescriptors The sort descriptors by which the array should be ordered. + * @param model A custom class that FDataSnapshots are coerced to, defaults to + * FDataSnapshot if nil + * @param identifier A string to use as a CellReuseIdentifier + * @param collectionView An instance of a UICollectionView to bind to + * @return An instance of FirebaseCollectionViewDataSource that populates + * UICollectionViewCells with a custom + * model class + */ +- (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + sortDescriptors:(__NULLABLE NSArray *)sortDescriptors + modelClass:(__NULLABLE Class)model + cellReuseIdentifier:(__NON_NULL NSString *)identifier + view:(__NON_NULL UICollectionView *)collectionView; + +/** + * Initialize an instance of FirebaseCollectionViewDataSource that populates + * UICollectionViewCells with a + * custom model class. Note that this method is used when using prototype cells, + * where the cells + * don't need to be registered in the class. + * @param ref A Firebase reference to bind the datasource to + * @param sortDescriptors The sort descriptors by which the array should be ordered. + * @param model A custom class that FDataSnapshots are coerced to, defaults to + * FDataSnapshot if nil + * @param identifier A string to use as a CellReuseIdentifier + * @param collectionView An instance of a UICollectionView to bind to + * @return An instance of FirebaseCollectionViewDataSource that populates + * UICollectionViewCells with a custom + * model class + */ +- (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + sortDescriptors:(__NULLABLE NSArray *)sortDescriptors + modelClass:(__NULLABLE Class)model + prototypeReuseIdentifier:(__NON_NULL NSString *)identifier + view:(__NON_NULL UICollectionView *)collectionView; + +/** + * Initialize an instance of FirebaseCollectionViewDataSource that populates a custom + * subclass of + * UICollectionViewCell with a custom model class. + * @param ref A Firebase reference to bind the datasource to + * @param sortDescriptors The sort descriptors by which the array should be ordered. + * @param model A custom class that FDataSnapshots are coerced to, defaults to + * FDataSnapshot if nil + * @param cell A subclass of UICollectionViewCell used to populate the UICollectionView, + * defaults to + * UICollectionViewCell if nil + * @param identifier A string to use as a CellReuseIdentifier + * @param collectionView An instance of a UICollectionView to bind to + * @return An instance of FirebaseCollectionViewDataSource that populates a custom + * subclass of + * UICollectionViewCell with a custom model class + */ +- (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + sortDescriptors:(__NULLABLE NSArray *)sortDescriptors + modelClass:(__NULLABLE Class)model + cellClass:(__NULLABLE Class)cell + cellReuseIdentifier:(__NON_NULL NSString *)identifier + view:(__NON_NULL UICollectionView *)collectionView; + +/** + * Initialize an instance of FirebaseCollectionViewDataSource that populates a custom + * xib with a custom + * model class. + * @param ref A Firebase reference to bind the datasource to + * @param sortDescriptors The sort descriptors by which the array should be ordered. + * @param model A custom class that FDataSnapshots are coerced to, defaults to + * FDataSnapshot if nil + * @param nibName The name of a xib file to create the layout for a + * UICollectionViewCell + * @param identifier A string to use as a CellReuseIdentifier + * @param collectionView An instance of a UICollectionView to bind to + * @return An instance of FirebaseCollectionViewDataSource that populates a custom + * xib with a custom + * model class + */ +- (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + sortDescriptors:(__NULLABLE NSArray *)sortDescriptors + modelClass:(__NULLABLE Class)model + nibNamed:(__NON_NULL NSString *)nibName + cellReuseIdentifier:(__NON_NULL NSString *)identifier + view:(__NON_NULL UICollectionView *)collectionView; + + /** * Initialize an instance of FirebaseCollectionViewDataSource that populates @@ -263,6 +430,84 @@ view:(__NON_NULL UICollectionView *) collectionView; +/** + * Initialize an instance of FirebaseCollectionViewDataSource that populates a custom + * xib with a custom + * model class. + * @param ref A Firebase reference to bind the datasource to + * @param predicate The predicate by which the array should be sorted. + * @param sortDescriptors The sort descriptors by which the array should be ordered. + * @param model A custom class that FDataSnapshots are coerced to, defaults to + * FDataSnapshot if nil + * @param nibName The name of a xib file to create the layout for a + * UICollectionViewCell + * @param identifier A string to use as a CellReuseIdentifier + * @param collectionView An instance of a UICollectionView to bind to + * @return An instance of FirebaseCollectionViewDataSource that populates a custom + * xib with a custom + * model class + */ +- (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + predicate:(__NULLABLE NSPredicate *)predicate + sortDescriptors:(__NULLABLE NSArray *)sortDescriptors + modelClass:(__NULLABLE Class)model + nibNamed:(__NON_NULL NSString *)nibName + cellReuseIdentifier:(__NON_NULL NSString *)identifier + view:(__NON_NULL UICollectionView *)collectionView; + + +/** + * Initialize an instance of FirebaseCollectionViewDataSource that populates + * UICollectionViewCells with a + * custom model class. Note that this method is used when using prototype cells, + * where the cells + * don't need to be registered in the class. + * @param ref A Firebase reference to bind the datasource to + * @param predicate The predicate by which the array should be sorted. + * @param sortDescriptors The sort descriptors by which the array should be ordered. + * @param model A custom class that FDataSnapshots are coerced to, defaults to + * FDataSnapshot if nil + * @param identifier A string to use as a CellReuseIdentifier + * @param collectionView An instance of a UICollectionView to bind to + * @return An instance of FirebaseCollectionViewDataSource that populates + * UICollectionViewCells with a custom + * model class + */ +-(__NULLABLE instancetype)initWithRef:(__NON_NULL Firebase *)ref + predicate:(__NULLABLE NSPredicate *)predicate + sortDescriptors:(__NULLABLE NSArray *)sortDescriptors + modelClass:(__NULLABLE Class)model + prototypeReuseIdentifier:(__NON_NULL NSString *)identifier + view:(__NON_NULL UICollectionView *)collectionView; + + +/** + * Initialize an instance of FirebaseCollectionViewDataSource that populates a custom + * subclass of + * UICollectionViewCell with a custom model class. + * @param ref A Firebase reference to bind the datasource to + * @param predicate The predicate by which the array should be sorted. + * @param sortDescriptors The sort descriptors by which the array should be ordered. + * @param model A custom class that FDataSnapshots are coerced to, defaults to + * FDataSnapshot if nil + * @param cell A subclass of UICollectionViewCell used to populate the UICollectionView, + * defaults to + * UICollectionViewCell if nil + * @param identifier A string to use as a CellReuseIdentifier + * @param collectionView An instance of a UICollectionView to bind to + * @return An instance of FirebaseCollectionViewDataSource that populates a custom + * subclass of + * UICollectionViewCell with a custom model class + */ +-(__NULLABLE instancetype)initWithRef:(__NON_NULL Firebase *)ref + predicate:(__NULLABLE NSPredicate *)predicate + sortDescriptors:(__NULLABLE NSArray *)sortDescriptors + modelClass:(__NULLABLE Class)model + cellClass:(__NULLABLE Class)cell + cellReuseIdentifier:(__NON_NULL NSString *)identifier + view:(__NON_NULL UICollectionView *)collectionView; + + /** * This method populates the fields of a UICollectionViewCell or subclass given * an FDataSnapshot (or diff --git a/FirebaseUI/API/FirebaseDataSource.h b/FirebaseUI/API/FirebaseDataSource.h index 9e789853c33..651910724e1 100644 --- a/FirebaseUI/API/FirebaseDataSource.h +++ b/FirebaseUI/API/FirebaseDataSource.h @@ -61,11 +61,13 @@ /** * Pass through of [FirebaseArray objectAtIndex:]. */ -- (id)objectAtIndex:(NSUInteger)index; +- (id)objectAtIndexPath:(NSIndexPath *)indexPath; /** * Pass through of [FirebaseArray refForIndex:]. */ -- (Firebase *)refForIndex:(NSUInteger)index; +- (Firebase *)refForIndexPath:(NSIndexPath *)indexPath; + +- (NSString *)sectionTitleForSection:(NSUInteger)section; @end diff --git a/FirebaseUI/API/FirebaseTableViewDataSource.h b/FirebaseUI/API/FirebaseTableViewDataSource.h index 80c281ba826..1a950e02e11 100644 --- a/FirebaseUI/API/FirebaseTableViewDataSource.h +++ b/FirebaseUI/API/FirebaseTableViewDataSource.h @@ -97,6 +97,7 @@ * UITableViewCells with * FDataSnapshots. * @param ref A Firebase reference to bind the datasource to + * @param sortDescriptors The sort descriptors by which the array should be ordered. * @param identifier A string to use as a CellReuseIdentifier * @param tableView An instance of a UITableView to bind to * @return An instance of FirebaseTableViewDataSource that populates @@ -104,6 +105,7 @@ * FDataSnapshots */ - (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + sortDescriptors:(__NULLABLE NSArray *)sortDescriptors cellReuseIdentifier:(__NON_NULL NSString *)identifier view:(__NON_NULL UITableView *)tableView; @@ -121,6 +123,7 @@ * FDataSnapshots */ - (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + sortDescriptors:(__NULLABLE NSArray *)sortDescriptors prototypeReuseIdentifier:(__NON_NULL NSString *)identifier view:(__NON_NULL UITableView *)tableView; @@ -129,6 +132,7 @@ * subclass of * UITableViewCell with FDataSnapshots. * @param ref A Firebase reference to bind the datasource to + * @param sortDescriptors The sort descriptors by which the array should be ordered. * @param cell A subclass of UITableViewCell used to populate the UITableView, * defaults to * UITableViewCell if nil @@ -139,6 +143,7 @@ * UITableViewCell with FDataSnapshots */ - (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + sortDescriptors:(__NULLABLE NSArray *)sortDescriptors cellClass:(__NULLABLE Class)cell cellReuseIdentifier:(__NON_NULL NSString *)identifier view:(__NON_NULL UITableView *)tableView; @@ -148,6 +153,7 @@ * xib with * FDataSnapshots. * @param ref A Firebase reference to bind the datasource to + * @param sortDescriptors The sort descriptors by which the array should be ordered. * @param nibName The name of a xib file to create the layout for a * UITableViewCell * @param identifier A string to use as a CellReuseIdentifier @@ -157,6 +163,7 @@ * FDataSnapshots */ - (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + sortDescriptors:(__NULLABLE NSArray *)sortDescriptors nibNamed:(__NON_NULL NSString *)nibName cellReuseIdentifier:(__NON_NULL NSString *)identifier view:(__NON_NULL UITableView *)tableView; @@ -166,6 +173,7 @@ * UITableViewCells with a * custom model class. * @param ref A Firebase reference to bind the datasource to + * @param sortDescriptors The sort descriptors by which the array should be ordered. * @param model A custom class that FDataSnapshots are coerced to, defaults to * FDataSnapshot if nil * @param identifier A string to use as a CellReuseIdentifier @@ -175,6 +183,7 @@ * model class */ - (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + sortDescriptors:(__NULLABLE NSArray *)sortDescriptors modelClass:(__NULLABLE Class)model cellReuseIdentifier:(__NON_NULL NSString *)identifier view:(__NON_NULL UITableView *)tableView; @@ -186,6 +195,7 @@ * where the cells * don't need to be registered in the class. * @param ref A Firebase reference to bind the datasource to + * @param sortDescriptors The sort descriptors by which the array should be ordered. * @param model A custom class that FDataSnapshots are coerced to, defaults to * FDataSnapshot if nil * @param identifier A string to use as a CellReuseIdentifier @@ -195,6 +205,7 @@ * model class */ - (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + sortDescriptors:(__NULLABLE NSArray *)sortDescriptors modelClass:(__NULLABLE Class)model prototypeReuseIdentifier:(__NON_NULL NSString *)identifier view:(__NON_NULL UITableView *)tableView; @@ -204,6 +215,7 @@ * subclass of * UITableViewCell with a custom model class. * @param ref A Firebase reference to bind the datasource to + * @param sortDescriptors The sort descriptors by which the array should be ordered. * @param model A custom class that FDataSnapshots are coerced to, defaults to * FDataSnapshot if nil * @param cell A subclass of UITableViewCell used to populate the UITableView, @@ -216,6 +228,7 @@ * UITableViewCell with a custom model class */ - (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + sortDescriptors:(__NULLABLE NSArray *)sortDescriptors modelClass:(__NULLABLE Class)model cellClass:(__NULLABLE Class)cell cellReuseIdentifier:(__NON_NULL NSString *)identifier @@ -226,6 +239,7 @@ * xib with a custom * model class. * @param ref A Firebase reference to bind the datasource to + * @param sortDescriptors The sort descriptors by which the array should be ordered. * @param model A custom class that FDataSnapshots are coerced to, defaults to * FDataSnapshot if nil * @param nibName The name of a xib file to create the layout for a @@ -237,11 +251,239 @@ * model class */ - (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + sortDescriptors:(__NULLABLE NSArray *)sortDescriptors modelClass:(__NULLABLE Class)model nibNamed:(__NON_NULL NSString *)nibName cellReuseIdentifier:(__NON_NULL NSString *)identifier view:(__NON_NULL UITableView *)tableView; +/** + * Initialize an instance of FirebaseTableViewDataSource that populates + * UITableViewCells with + * FDataSnapshots. + * @param ref A Firebase reference to bind the datasource to + * @param identifier A string to use as a CellReuseIdentifier + * @param tableView An instance of a UITableView to bind to + * @return An instance of FirebaseTableViewDataSource that populates + * UITableViewCells with + * FDataSnapshots + */ +- (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + cellReuseIdentifier:(__NON_NULL NSString *)identifier + view:(__NON_NULL UITableView *)tableView; + +/** + * Initialize an instance of FirebaseTableViewDataSource that populates + * UITableViewCells with + * FDataSnapshots. Note that this method is used when using prototype cells, + * where the cells don't + * need to be registered in the class. + * @param ref A Firebase reference to bind the datasource to + * @param identifier A string to use as a CellReuseIdentifier + * @param tableView An instance of a UITableView to bind to + * @return An instance of FirebaseTableViewDataSource that populates + * UITableViewCells with + * FDataSnapshots + */ +- (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + prototypeReuseIdentifier:(__NON_NULL NSString *)identifier + view:(__NON_NULL UITableView *)tableView; + +/** + * Initialize an instance of FirebaseTableViewDataSource that populates a custom + * subclass of + * UITableViewCell with FDataSnapshots. + * @param ref A Firebase reference to bind the datasource to + * @param cell A subclass of UITableViewCell used to populate the UITableView, + * defaults to + * UITableViewCell if nil + * @param identifier A string to use as a CellReuseIdentifier + * @param tableView An instance of a UITableView to bind to + * @return An instance of FirebaseTableViewDataSource that populates a custom + * subclass of + * UITableViewCell with FDataSnapshots + */ +- (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + cellClass:(__NULLABLE Class)cell + cellReuseIdentifier:(__NON_NULL NSString *)identifier + view:(__NON_NULL UITableView *)tableView; + +/** + * Initialize an instance of FirebaseTableViewDataSource that populates a custom + * xib with + * FDataSnapshots. + * @param ref A Firebase reference to bind the datasource to + * @param nibName The name of a xib file to create the layout for a + * UITableViewCell + * @param identifier A string to use as a CellReuseIdentifier + * @param tableView An instance of a UITableView to bind to + * @return An instance of FirebaseTableViewDataSource that populates a custom + * xib with + * FDataSnapshots + */ +- (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + nibNamed:(__NON_NULL NSString *)nibName + cellReuseIdentifier:(__NON_NULL NSString *)identifier + view:(__NON_NULL UITableView *)tableView; + +/** + * Initialize an instance of FirebaseTableViewDataSource that populates + * UITableViewCells with a + * custom model class. + * @param ref A Firebase reference to bind the datasource to + * @param model A custom class that FDataSnapshots are coerced to, defaults to + * FDataSnapshot if nil + * @param identifier A string to use as a CellReuseIdentifier + * @param tableView An instance of a UITableView to bind to + * @return An instance of FirebaseTableViewDataSource that populates + * UITableViewCells with a custom + * model class + */ +- (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + modelClass:(__NULLABLE Class)model + cellReuseIdentifier:(__NON_NULL NSString *)identifier + view:(__NON_NULL UITableView *)tableView; + +/** + * Initialize an instance of FirebaseTableViewDataSource that populates + * UITableViewCells with a + * custom model class. Note that this method is used when using prototype cells, + * where the cells + * don't need to be registered in the class. + * @param ref A Firebase reference to bind the datasource to + * @param model A custom class that FDataSnapshots are coerced to, defaults to + * FDataSnapshot if nil + * @param identifier A string to use as a CellReuseIdentifier + * @param tableView An instance of a UITableView to bind to + * @return An instance of FirebaseTableViewDataSource that populates + * UITableViewCells with a custom + * model class + */ +- (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + modelClass:(__NULLABLE Class)model + prototypeReuseIdentifier:(__NON_NULL NSString *)identifier + view:(__NON_NULL UITableView *)tableView; + +/** + * Initialize an instance of FirebaseTableViewDataSource that populates a custom + * subclass of + * UITableViewCell with a custom model class. + * @param ref A Firebase reference to bind the datasource to + * @param model A custom class that FDataSnapshots are coerced to, defaults to + * FDataSnapshot if nil + * @param cell A subclass of UITableViewCell used to populate the UITableView, + * defaults to + * UITableViewCell if nil + * @param identifier A string to use as a CellReuseIdentifier + * @param tableView An instance of a UITableView to bind to + * @return An instance of FirebaseTableViewDataSource that populates a custom + * subclass of + * UITableViewCell with a custom model class + */ +- (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + modelClass:(__NULLABLE Class)model + cellClass:(__NULLABLE Class)cell + cellReuseIdentifier:(__NON_NULL NSString *)identifier + view:(__NON_NULL UITableView *)tableView; + +/** + * Initialize an instance of FirebaseTableViewDataSource that populates a custom + * xib with a custom + * model class. + * @param ref A Firebase reference to bind the datasource to + * @param model A custom class that FDataSnapshots are coerced to, defaults to + * FDataSnapshot if nil + * @param nibName The name of a xib file to create the layout for a + * UITableViewCell + * @param identifier A string to use as a CellReuseIdentifier + * @param tableView An instance of a UITableView to bind to + * @return An instance of FirebaseTableViewDataSource that populates a custom + * xib with a custom + * model class + */ +- (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + modelClass:(__NULLABLE Class)model + nibNamed:(__NON_NULL NSString *)nibName + cellReuseIdentifier:(__NON_NULL NSString *)identifier + view:(__NON_NULL UITableView *)tableView; + +/** + * Initialize an instance of FirebaseTableViewDataSource that populates a custom + * xib with a custom + * model class. + * @param ref A Firebase reference to bind the datasource to + * @param predicate The predicate by which the array should be sorted. + * @param sortDescriptors The sort descriptors by which the array should be ordered. + * @param model A custom class that FDataSnapshots are coerced to, defaults to + * FDataSnapshot if nil + * @param nibName The name of a xib file to create the layout for a + * UITableViewCell + * @param identifier A string to use as a CellReuseIdentifier + * @param tableView An instance of a UITableView to bind to + * @return An instance of FirebaseTableViewDataSource that populates a custom + * xib with a custom + * model class + */ +- (__NON_NULL instancetype)initWithRef:(__NON_NULL Firebase *)ref + predicate:(__NULLABLE NSPredicate *)predicate + sortDescriptors:(__NULLABLE NSArray *)sortDescriptors + modelClass:(__NULLABLE Class)model + nibNamed:(__NON_NULL NSString *)nibName + cellReuseIdentifier:(__NON_NULL NSString *)identifier + view:(__NON_NULL UITableView *)tableView; + + +/** + * Initialize an instance of FirebaseTableViewDataSource that populates + * UITableViewCells with a + * custom model class. Note that this method is used when using prototype cells, + * where the cells + * don't need to be registered in the class. + * @param ref A Firebase reference to bind the datasource to + * @param predicate The predicate by which the array should be sorted. + * @param sortDescriptors The sort descriptors by which the array should be ordered. + * @param model A custom class that FDataSnapshots are coerced to, defaults to + * FDataSnapshot if nil + * @param identifier A string to use as a CellReuseIdentifier + * @param tableView An instance of a UITableView to bind to + * @return An instance of FirebaseTableViewDataSource that populates + * UITableViewCells with a custom + * model class + */ +-(__NULLABLE instancetype)initWithRef:(__NON_NULL Firebase *)ref + predicate:(__NULLABLE NSPredicate *)predicate + sortDescriptors:(__NULLABLE NSArray *)sortDescriptors + modelClass:(__NULLABLE Class)model + prototypeReuseIdentifier:(__NON_NULL NSString *)identifier + view:(__NON_NULL UITableView *)tableView; + + +/** + * Initialize an instance of FirebaseTableViewDataSource that populates a custom + * subclass of + * UITableViewCell with a custom model class. + * @param ref A Firebase reference to bind the datasource to + * @param predicate The predicate by which the array should be sorted. + * @param sortDescriptors The sort descriptors by which the array should be ordered. + * @param model A custom class that FDataSnapshots are coerced to, defaults to + * FDataSnapshot if nil + * @param cell A subclass of UITableViewCell used to populate the UITableView, + * defaults to + * UITableViewCell if nil + * @param identifier A string to use as a CellReuseIdentifier + * @param tableView An instance of a UITableView to bind to + * @return An instance of FirebaseTableViewDataSource that populates a custom + * subclass of + * UITableViewCell with a custom model class + */ +-(__NULLABLE instancetype)initWithRef:(__NON_NULL Firebase *)ref + predicate:(__NULLABLE NSPredicate *)predicate + sortDescriptors:(__NULLABLE NSArray *)sortDescriptors + modelClass:(__NULLABLE Class)model + cellClass:(__NULLABLE Class)cell + cellReuseIdentifier:(__NON_NULL NSString *)identifier + view:(__NON_NULL UITableView *)tableView; + /** * This method populates the fields of a UITableViewCell or subclass given a * model object (or diff --git a/FirebaseUI/Implementation/FirebaseArray.m b/FirebaseUI/Implementation/FirebaseArray.m index 675aa2fb901..fd42ef2110d 100644 --- a/FirebaseUI/Implementation/FirebaseArray.m +++ b/FirebaseUI/Implementation/FirebaseArray.m @@ -32,9 +32,19 @@ #import +#import + #import "FirebaseArray.h" -@implementation FirebaseArray +@implementation FirebaseArray { + BOOL _initStarted; + BOOL _initialized; + FirebaseHandle _addHandle; + FirebaseHandle _changeHandle; + FirebaseHandle _removeHandle; + FirebaseHandle _moveHandle; + FirebaseHandle _valueHandle; +} #pragma mark - #pragma mark Initializer methods @@ -46,6 +56,7 @@ - (instancetype)initWithRef:(Firebase *)ref { - (instancetype)initWithQuery:(FQuery *)query { self = [super init]; if (self) { + self.sectionValues = [NSMutableOrderedSet orderedSet]; self.snapshots = [NSMutableArray array]; self.query = query; @@ -54,95 +65,440 @@ - (instancetype)initWithQuery:(FQuery *)query { return self; } +-(instancetype)initWithRef:(Firebase *)ref sortDescriptors:(NSArray *)sortDescriptors { + return [self initWithQuery:ref sortDescriptors:sortDescriptors predicate:nil]; +} + +-(instancetype)initWithQuery:(FQuery *)query sortDescriptors:(NSArray *)sortDescriptors { + return [self initWithQuery:query sortDescriptors:sortDescriptors predicate:nil]; +} + +-(instancetype)initWithQuery:(FQuery *)query predicate:(NSPredicate *)predicate { + return [self initWithQuery:query sortDescriptors:nil predicate:predicate]; +} + +-(instancetype)initWithQuery:(FQuery *)query + sortDescriptors:(NSArray *)sortDescriptors + predicate:(NSPredicate *)predicate { + self = [super init]; + if (!self) { + return nil; + } + self.sectionValues = [NSMutableOrderedSet orderedSet]; + self.snapshots = [NSMutableArray array]; + self.query = query; + self.sortDescriptors = sortDescriptors; + self.predicate = predicate; + + [self initListeners]; + + return self; +} + #pragma mark - #pragma mark Memory management methods - (void)dealloc { - // TODO: Consider keeping track of these and only removing them if they are - // explicitly added here - [self.query removeAllObservers]; + [self.query removeObserverWithHandle:_addHandle]; + [self.query removeObserverWithHandle:_changeHandle]; + [self.query removeObserverWithHandle:_removeHandle]; + [self.query removeObserverWithHandle:_moveHandle]; + [self.query removeObserverWithHandle:_valueHandle]; } #pragma mark - #pragma mark Private API methods - (void)initListeners { - [self.query observeEventType:FEventTypeChildAdded - andPreviousSiblingKeyWithBlock:^(FDataSnapshot *snapshot, - NSString *previousChildKey) { - NSUInteger index = [self indexForKey:previousChildKey] + 1; - - [self.snapshots insertObject:snapshot atIndex:index]; - - [self.delegate childAdded:snapshot atIndex:index]; - }]; + [self initAddListener]; + [self initChangeListener]; + [self initRemoveListener]; + [self initMoveListener]; + [self initValueListener]; +} - [self.query observeEventType:FEventTypeChildChanged - andPreviousSiblingKeyWithBlock:^(FDataSnapshot *snapshot, - NSString *previousChildKey) { - NSUInteger index = [self indexForKey:snapshot.key]; +- (void)initValueListener { + _valueHandle = [self.query observeEventType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) { + if (_initialized) { + return; + } + + if (self.sectionKeyPath) { + NSUInteger count = self.sectionValues.count; + + NSIndexSet * sections = [NSIndexSet + indexSetWithIndexesInRange:NSMakeRange(0, count)]; + + [self.delegate sectionsAddedAtIndexes:sections]; + } else { + [self.delegate sectionsAddedAtIndexes:[NSIndexSet indexSetWithIndex:0]]; + } + + _initialized = YES; + [self.delegate endUpdates]; + }]; +} - [self.snapshots replaceObjectAtIndex:index withObject:snapshot]; +- (void)initAddListener { + _addHandle = [self.query observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) { + if (!_initialized && !_initStarted) { + _initStarted = YES; + [self.delegate beginUpdates]; + } + if (self.predicate && ![self.predicate evaluateWithObject:snapshot]) { + return; + } + + NSUInteger index = [self insertionIndexForSnapshot:snapshot]; + + [self.snapshots insertObject:snapshot atIndex:index]; + + if (self.sectionKeyPath) { + id sectionKeyValue = [snapshot valueForKeyPath:self.sectionKeyPath]; + + if (![self.sectionValues containsObject:sectionKeyValue]) { + NSUInteger sectionIndex = + [self.sectionValues indexOfObject:sectionKeyValue + inSortedRange:NSMakeRange(0, self.sectionValues.count) + options: + NSBinarySearchingInsertionIndex | NSBinarySearchingFirstEqual + usingComparator:^NSComparisonResult(id obj1, id obj2) { + return self.sectionsOrderedAscending? [obj2 compare:obj1] :[obj1 compare:obj2]; + }]; + + [self.sectionValues insertObject:sectionKeyValue atIndex:sectionIndex]; + + if (_initialized) [self.delegate sectionAddedAtSectionIndex:sectionIndex]; + + return; + } + } + + NSIndexPath * indexPath = [self indexPathOfObject:snapshot]; + + if (_initialized) [self.delegate childAdded:snapshot atIndexPath:indexPath]; + }]; +} - [self.delegate childChanged:snapshot atIndex:index]; - }]; +- (void)initRemoveListener { + _removeHandle = [self.query observeEventType:FEventTypeChildRemoved withBlock:^(FDataSnapshot *snapshot) { + if (!_initialized && !_initStarted) { + _initStarted = YES; + [self.delegate beginUpdates]; + } + NSUInteger index = [self indexInSnapshotsForKey:snapshot.key]; + + if (index == NSNotFound) { + return; + } + + NSIndexPath * indexPath = [self indexPathOfObject:snapshot]; + + [self.snapshots removeObjectAtIndex:index]; + + if (self.sectionKeyPath && ![self sectionAtIndex:indexPath.section].count) { + [self.sectionValues removeObjectAtIndex:indexPath.section]; + + if (_initialized) [self.delegate sectionRemovedAtSectionIndex:indexPath.section]; + } else { + if (_initialized) [self.delegate childRemoved:snapshot atIndexPath:indexPath]; + } + }]; +} - [self.query observeEventType:FEventTypeChildRemoved - withBlock:^(FDataSnapshot *snapshot) { - NSUInteger index = [self indexForKey:snapshot.key]; +- (void)handleChange:(FDataSnapshot *)snapshot { + if (!_initialized && !_initStarted) { + _initStarted = YES; + [self.delegate beginUpdates]; + } + + NSUInteger startingIndexInSnapshots = [self indexInSnapshotsForKey:snapshot.key]; + + NSIndexPath * startingIndexPath = [self indexPathForKey:snapshot.key]; + + id startingSectionKeyValue; + id newSectionKeyValue; + + BOOL sectionRemoved = NO; + + if (startingIndexInSnapshots != NSNotFound) { + FDataSnapshot * startingSnapshot = self.snapshots[startingIndexInSnapshots]; + + [self.snapshots removeObjectAtIndex:startingIndexInSnapshots]; + + + NSUInteger startingSectionIndex = 0; + + if (self.sectionKeyPath) { + startingSectionKeyValue = [startingSnapshot valueForKeyPath:self.sectionKeyPath]; + + startingSectionIndex = [self.sectionValues indexOfObject:startingSectionKeyValue]; + + newSectionKeyValue = [snapshot valueForKeyPath:self.sectionKeyPath]; + + if (![startingSectionKeyValue isEqual:newSectionKeyValue] && + ![self sectionAtIndex:startingSectionIndex].count) { + + [self.sectionValues removeObjectAtIndex:startingSectionIndex]; + + sectionRemoved = YES; + } + } + + + if (sectionRemoved) { + if (_initialized) [self.delegate sectionRemovedAtSectionIndex:startingSectionIndex]; + } else { + if (_initialized) [self.delegate childRemoved:startingSnapshot atIndexPath:startingIndexPath]; + } + } + + + if (self.predicate && ![self.predicate evaluateWithObject:snapshot]) { + return; + } + + + BOOL sectionInserted; + + if (self.sectionKeyPath && ![self.sectionValues containsObject:newSectionKeyValue]) { + NSUInteger newSectionIndex = + [self.sectionValues indexOfObject:newSectionKeyValue + inSortedRange:NSMakeRange(0, self.sectionValues.count) + options: + NSBinarySearchingInsertionIndex | NSBinarySearchingFirstEqual + usingComparator:^NSComparisonResult(id obj1, id obj2) { + return self.sectionsOrderedAscending? [obj2 compare:obj1] :[obj1 compare:obj2]; + }]; + + [self.sectionValues insertObject:newSectionKeyValue atIndex:newSectionIndex]; + + sectionInserted = YES; + } + + + NSUInteger newSortedIndex = [self insertionIndexForSnapshot:snapshot]; + + [self.snapshots insertObject:snapshot atIndex:newSortedIndex]; + + NSIndexPath * newIndexPath = [self indexPathOfObject:snapshot]; + + if (sectionInserted) { + if (_initialized) [self.delegate sectionAddedAtSectionIndex:newIndexPath.section]; + } else +// if (startingIndexInSnapshots == NSNotFound || sectionRemoved) + { + if (_initialized) [self.delegate childAdded:snapshot atIndexPath:newIndexPath]; + } +// else if (startingIndexInSnapshots == newSortedIndex) { +// if (_initialized) [self.delegate childChanged:snapshot atIndexPath:startingIndexPath]; +// } else { +// if (_initialized) [self.delegate childMoved:snapshot fromIndexPath:startingIndexPath toIndexPath:newIndexPath]; +// } +} - [self.snapshots removeObjectAtIndex:index]; +- (void)initChangeListener { + _changeHandle = [self.query observeEventType:FEventTypeChildChanged withBlock:^(FDataSnapshot *snapshot) { + [self handleChange:snapshot]; + }]; +} - [self.delegate childRemoved:snapshot atIndex:index]; - }]; +- (void)initMoveListener { + _moveHandle = [self.query observeEventType:FEventTypeChildMoved withBlock:^(FDataSnapshot *snapshot) { + [self handleChange:snapshot]; + }]; +} - [self.query observeEventType:FEventTypeChildMoved - andPreviousSiblingKeyWithBlock:^(FDataSnapshot *snapshot, - NSString *previousChildKey) { - NSUInteger fromIndex = [self indexForKey:snapshot.key]; - [self.snapshots removeObjectAtIndex:fromIndex]; +#pragma mark - +#pragma mark Searching Methods + +- (NSUInteger)indexInSnapshotsForKey:(NSString *)key { + if (!key) return NSNotFound; + + NSUInteger index = + [self.snapshots + indexOfObjectWithOptions:NSEnumerationConcurrent + passingTest:^BOOL(FDataSnapshot * obj, NSUInteger idx, BOOL * stop) { + BOOL result = [key isEqualToString:obj.key]; + if (result) { + *stop = YES; + } + return result; + }]; + + return index; +} - NSUInteger toIndex = [self indexForKey:previousChildKey] + 1; - [self.snapshots insertObject:snapshot atIndex:toIndex]; +- (NSIndexPath *)indexPathForKey:(NSString *)key { + if (!key) return [NSIndexPath indexPathForRow:NSNotFound inSection:NSNotFound]; + + NSUInteger indexInSnapshots = [self indexInSnapshotsForKey:key]; + + if (indexInSnapshots == NSNotFound) { + return nil; + } + + return [self indexPathOfObject:self.snapshots[indexInSnapshots]]; +} - [self.delegate childMoved:snapshot fromIndex:fromIndex toIndex:toIndex]; - }]; +-(NSIndexPath *)indexPathOfObject:(FDataSnapshot *)snapshot { + NSUInteger sectionIndex; + + if (self.sectionKeyPath) { + id sectionKeyValue = [snapshot valueForKeyPath:self.sectionKeyPath]; + sectionIndex = [self.sectionValues indexOfObject:sectionKeyValue]; + + if (sectionIndex == NSNotFound) { + NSString *errorReason = + [NSString stringWithFormat:@"Section \"%@\" not found in SectionValues %@", + sectionKeyValue, self.sectionValues]; + @throw [NSException exceptionWithName:@"FirebaseArraySectionNotFoundException" + reason:errorReason + userInfo:@{ + @"Section" : sectionKeyValue, + @"SectionValues" : self.sectionValues + }]; + } + } else { + sectionIndex = 0; + } + + NSArray * section = [self sectionAtIndex:sectionIndex]; + NSUInteger rowIndex = + [section + indexOfObjectWithOptions:NSEnumerationConcurrent + passingTest:^BOOL(FDataSnapshot * obj, NSUInteger idx, BOOL * stop) { + BOOL result = [snapshot.key isEqualToString:obj.key]; + if (result) { + *stop = YES; + } + return result; + }]; + + if (rowIndex == NSNotFound) { + + } + + return [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex]; } -- (NSUInteger)indexForKey:(NSString *)key { - if (!key) return -1; +- (NSArray *)sectionAtIndex:(NSUInteger)sectionIndex { + NSString * sectionKeyPath = self.sectionKeyPath; + + if (!sectionKeyPath) { + if (sectionIndex == 0) { + return self.snapshots; + } else { + return [NSArray array]; + } + } + + id sectionKeyValue = self.sectionValues[sectionIndex]; + + NSUInteger firstIndex = [self.snapshots indexOfObject:sectionKeyValue + inSortedRange:NSMakeRange(0, self.snapshots.count) + options:NSBinarySearchingFirstEqual + usingComparator:^NSComparisonResult(id obj1, + id obj2) { + NSComparisonResult result; + if ([obj1 isKindOfClass:[sectionKeyValue class]]) { + result = [obj1 compare:[obj2 valueForKeyPath:sectionKeyPath]]; + } else { + result = [[obj1 valueForKeyPath:sectionKeyPath] compare:obj2]; + } + return result; + }]; + if (firstIndex == NSNotFound) { + return [NSArray array]; + } + + NSUInteger lastIndex = [self.snapshots indexOfObject:sectionKeyValue + inSortedRange:NSMakeRange(0, self.snapshots.count) + options:NSBinarySearchingLastEqual + usingComparator:^NSComparisonResult(id obj1, + id obj2) { + NSComparisonResult result; + if ([obj1 isKindOfClass:[sectionKeyValue class]]) { + result = [obj1 compare:[obj2 valueForKeyPath:sectionKeyPath]]; + } else { + result = [[obj1 valueForKeyPath:sectionKeyPath] compare:obj2]; + } + return result; + }]; + + NSRange sectionRange = NSMakeRange(firstIndex, lastIndex - firstIndex + 1); + + return [self.snapshots subarrayWithRange:sectionRange]; +} - for (NSUInteger index = 0; index < [self.snapshots count]; index++) { - if ([key isEqualToString:[(FDataSnapshot *)[self.snapshots - objectAtIndex:index] key]]) { - return index; +- (NSUInteger)insertionIndexForSnapshot:(FDataSnapshot *)snapshot { + if (!self.snapshots.count) { + return 0; } - } + return [self.snapshots indexOfObject:snapshot + inSortedRange:NSMakeRange(0, self.snapshots.count) + options: + NSBinarySearchingInsertionIndex | NSBinarySearchingFirstEqual + usingComparator:[self comparator]]; +} - NSString *errorReason = - [NSString stringWithFormat:@"Key \"%@\" not found in FirebaseArray %@", - key, self.snapshots]; - @throw [NSException exceptionWithName:@"FirebaseArrayKeyNotFoundException" - reason:errorReason - userInfo:@{ - @"Key" : key, - @"Array" : self.snapshots - }]; +- (NSComparator)comparator { + NSArray * sortDescriptors = [NSArray array]; + if (self.sectionKeyPath) { + sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:self.sectionKeyPath + ascending:self.sectionsOrderedAscending]]; + } + if (self.sortDescriptors.count) { + sortDescriptors = [sortDescriptors arrayByAddingObjectsFromArray:self.sortDescriptors]; + } + + if (sortDescriptors.count) { + return ^(FDataSnapshot * obj1, FDataSnapshot * obj2) { + NSArray * sort = sortDescriptors; + + if ([obj1.key isEqualToString:obj2.key]) { + return NSOrderedSame; + } + + NSComparisonResult result = NSOrderedSame; + for (NSSortDescriptor * sortDescriptor in sort) { + result = [sortDescriptor compareObject:obj1 toObject:obj2]; + if (result != NSOrderedSame) { + break; + } + } + return result; + }; + } + return ^(FDataSnapshot * obj1, FDataSnapshot * obj2) { + return [obj1.key compare:obj2.key]; + }; } #pragma mark - #pragma mark Public API methods - (NSUInteger)count { - return [self.snapshots count]; + return [self.snapshots count]; +} + +-(NSUInteger)numberOfSections { + if (!_initialized) { + return 0; + } + if (self.sectionKeyPath) { + return self.sectionValues.count; + } + return 1; } -- (FDataSnapshot *)objectAtIndex:(NSUInteger)index { - return (FDataSnapshot *)[self.snapshots objectAtIndex:index]; +- (FDataSnapshot *)objectAtIndexPath:(NSIndexPath *)indexPath { + NSArray * section = [self sectionAtIndex:indexPath.section]; + return section[indexPath.row]; } -- (Firebase *)refForIndex:(NSUInteger)index { - return [(FDataSnapshot *)[self.snapshots objectAtIndex:index] ref]; +- (Firebase *)refForIndexPath:(NSIndexPath *)indexPath { + return [self objectAtIndexPath:indexPath].ref; } @end diff --git a/FirebaseUI/Implementation/FirebaseCollectionViewDataSource.m b/FirebaseUI/Implementation/FirebaseCollectionViewDataSource.m index 0ccf0cc6fcf..7ad5ab156f7 100644 --- a/FirebaseUI/Implementation/FirebaseCollectionViewDataSource.m +++ b/FirebaseUI/Implementation/FirebaseCollectionViewDataSource.m @@ -39,6 +39,152 @@ @implementation FirebaseCollectionViewDataSource #pragma mark - #pragma mark FirebaseDataSource initializer methods +- (instancetype)initWithRef:(Firebase *)ref + sortDescriptors:(NSArray *)sortDescriptors + cellReuseIdentifier:(NSString *)identifier + view:(UICollectionView *)collectionView { + return [self initWithRef:ref + sortDescriptors:sortDescriptors + modelClass:nil + cellClass:nil + cellReuseIdentifier:identifier + view:collectionView]; +} + +-(instancetype)initWithRef:(Firebase *)ref + sortDescriptors:(NSArray *)sortDescriptors + prototypeReuseIdentifier:(NSString *)identifier + view:(UICollectionView *)collectionView { + self.hasPrototypeCell = YES; + return [self initWithRef:ref + sortDescriptors:sortDescriptors + modelClass:nil + cellClass:nil + cellReuseIdentifier:identifier + view:collectionView]; +} + +-(instancetype)initWithRef:(Firebase *)ref + sortDescriptors:(NSArray *)sortDescriptors + modelClass:(Class)model + cellReuseIdentifier:(NSString *)identifier + view:(UICollectionView *)collectionView { + return [self initWithRef:ref + sortDescriptors:sortDescriptors + modelClass:model + cellClass:nil + cellReuseIdentifier:identifier + view:collectionView]; +} + +-(instancetype)initWithRef:(Firebase *)ref + sortDescriptors:(NSArray *)sortDescriptors + nibNamed:(NSString *)nibName + cellReuseIdentifier:(NSString *)identifier + view:(UICollectionView *)collectionView { + return [self initWithRef:ref + sortDescriptors:sortDescriptors + modelClass:nil + nibNamed:nibName + cellReuseIdentifier:identifier + view:collectionView]; +} + +-(instancetype)initWithRef:(Firebase *)ref + sortDescriptors:(NSArray *)sortDescriptors + cellClass:(Class)cell + cellReuseIdentifier:(NSString *)identifier + view:(UICollectionView *)collectionView { + return [self initWithRef:ref + sortDescriptors:sortDescriptors + modelClass:nil + cellClass:cell + cellReuseIdentifier:identifier + view:collectionView]; + +} + +-(instancetype)initWithRef:(Firebase *)ref + sortDescriptors:(NSArray *)sortDescriptors + modelClass:(Class)model + prototypeReuseIdentifier:(NSString *)identifier + view:(UICollectionView *)collectionView { + self.hasPrototypeCell = YES; + return [self initWithRef:ref + sortDescriptors:sortDescriptors + modelClass:model + cellClass:nil + cellReuseIdentifier:identifier + view:collectionView]; +} + +-(instancetype)initWithRef:(Firebase *)ref + predicate:(NSPredicate *)predicate + sortDescriptors:(NSArray *)sortDescriptors + modelClass:(Class)model + cellClass:(Class)cell + cellReuseIdentifier:(NSString *)identifier + view:(UICollectionView *)collectionView { + FirebaseArray *array = [[FirebaseArray alloc] initWithQuery:ref + sortDescriptors:sortDescriptors + predicate:predicate]; + self = [super initWithArray:array]; + if (!self) { + return nil; + } + + if (!model) { + model = [FDataSnapshot class]; + } + + if (!cell) { + cell = [UICollectionViewCell class]; + } + + self.collectionView = collectionView; + self.modelClass = model; + self.reuseIdentifier = identifier; + self.populateCell = ^(id cell, id object) { + }; + + if (!self.hasPrototypeCell) { + [self.collectionView registerClass:self.cellClass + forCellWithReuseIdentifier:self.reuseIdentifier]; + } + return self; +} + +-(instancetype)initWithRef:(Firebase *)ref + predicate:(NSPredicate *)predicate + sortDescriptors:(NSArray *)sortDescriptors + modelClass:(Class)model + nibNamed:(NSString *)nibName + cellReuseIdentifier:(NSString *)identifier + view:(UICollectionView *)collectionView { + FirebaseArray *array = [[FirebaseArray alloc] initWithQuery:ref + sortDescriptors:sortDescriptors + predicate:predicate]; + self = [super initWithArray:array]; + if (!self) { + return nil; + } + + if (!model) { + model = [FDataSnapshot class]; + } + + self.collectionView = collectionView; + self.modelClass = model; + self.reuseIdentifier = identifier; + self.populateCell = ^(id cell, id object) { + }; + + UINib *nib = [UINib nibWithNibName:nibName bundle:nil]; + [self.collectionView registerNib:nib + forCellWithReuseIdentifier:self.reuseIdentifier]; + return self; +} + - (instancetype)initWithRef:(Firebase *)ref cellReuseIdentifier:(NSString *)identifier view:(UICollectionView *)collectionView { @@ -110,30 +256,12 @@ - (instancetype)initWithRef:(Firebase *)ref cellClass:(Class)cell cellReuseIdentifier:(NSString *)identifier view:(UICollectionView *)collectionView { - FirebaseArray *array = [[FirebaseArray alloc] initWithRef:ref]; - self = [super initWithArray:array]; - if (self) { - if (!model) { - model = [FDataSnapshot class]; - } - - if (!cell) { - cell = [UICollectionViewCell class]; - } - - self.collectionView = collectionView; - self.modelClass = model; - self.cellClass = cell; - self.reuseIdentifier = identifier; - self.populateCell = ^(id cell, id object) { - }; - - if (!self.hasPrototypeCell) { - [self.collectionView registerClass:self.cellClass - forCellWithReuseIdentifier:self.reuseIdentifier]; - } - } - return self; + return [self initWithRef:ref + sortDescriptors:nil + modelClass:model + cellClass:cell + cellReuseIdentifier:identifier + view:collectionView]; } - (instancetype)initWithRef:(Firebase *)ref @@ -141,24 +269,59 @@ - (instancetype)initWithRef:(Firebase *)ref nibNamed:(NSString *)nibName cellReuseIdentifier:(NSString *)identifier view:(UICollectionView *)collectionView { - FirebaseArray *array = [[FirebaseArray alloc] initWithRef:ref]; - self = [super initWithArray:array]; - if (self) { - if (!model) { - model = [FDataSnapshot class]; - } + + return [self initWithRef:ref + sortDescriptors:nil + modelClass:model + nibNamed:nibName + cellReuseIdentifier:identifier + view:collectionView]; +} - self.collectionView = collectionView; - self.modelClass = model; - self.reuseIdentifier = identifier; - self.populateCell = ^(id cell, id object) { - }; +-(instancetype)initWithRef:(Firebase *)ref + predicate:(NSPredicate *)predicate + sortDescriptors:(NSArray *)sortDescriptors + modelClass:(Class)model + prototypeReuseIdentifier:(NSString *)identifier + view:(UICollectionView *)collectionView { + self.hasPrototypeCell = YES; + return [self initWithRef:ref + predicate:predicate + sortDescriptors:sortDescriptors + modelClass:model + cellClass:nil + cellReuseIdentifier:identifier + view:collectionView]; +} - UINib *nib = [UINib nibWithNibName:nibName bundle:nil]; - [self.collectionView registerNib:nib - forCellWithReuseIdentifier:self.reuseIdentifier]; - } - return self; +-(instancetype)initWithRef:(Firebase *)ref + sortDescriptors:(NSArray *)sortDescriptors + modelClass:(Class)model + cellClass:(Class)cell + cellReuseIdentifier:(NSString *)identifier + view:(UICollectionView *)collectionView { + return [self initWithRef:ref + predicate:nil + sortDescriptors:sortDescriptors + modelClass:model + cellClass:cell + cellReuseIdentifier:identifier + view:collectionView]; +} + +-(instancetype)initWithRef:(Firebase *)ref + sortDescriptors:(NSArray *)sortDescriptors + modelClass:(Class)model + nibNamed:(NSString *)nibName + cellReuseIdentifier:(NSString *)identifier + view:(UICollectionView *)collectionView { + return [self initWithRef:ref + predicate:nil + sortDescriptors:sortDescriptors + modelClass:model + nibNamed:nibName + cellReuseIdentifier:identifier + view:collectionView]; } #pragma mark - @@ -200,7 +363,7 @@ - (void)childMoved:(id)obj dequeueReusableCellWithReuseIdentifier:self.reuseIdentifier forIndexPath:indexPath]; - FDataSnapshot *snap = [self.array objectAtIndex:indexPath.row]; + FDataSnapshot *snap = [self.array objectAtIndexPath:indexPath]; if (![self.modelClass isSubclassOfClass:[FDataSnapshot class]]) { id model = [[self.modelClass alloc] init]; // TODO: replace setValuesForKeysWithDictionary with client API diff --git a/FirebaseUI/Implementation/FirebaseDataSource.m b/FirebaseUI/Implementation/FirebaseDataSource.m index 30931bd3c5c..853326291e8 100644 --- a/FirebaseUI/Implementation/FirebaseDataSource.m +++ b/FirebaseUI/Implementation/FirebaseDataSource.m @@ -55,12 +55,20 @@ - (NSUInteger)count { return [self.array count]; } -- (id)objectAtIndex:(NSUInteger)index { - return [self.array objectAtIndex:index]; +- (id)objectAtIndexPath:(NSIndexPath *)indexPath { + return [self.array objectAtIndexPath:indexPath]; } -- (Firebase *)refForIndex:(NSUInteger)index { - return [self.array refForIndex:index]; +- (Firebase *)refForIndexPath:(NSIndexPath *)indexPath { + return [self.array refForIndexPath:indexPath]; +} + +-(NSString *)sectionTitleForSection:(NSUInteger)section { + if (!self.array.sectionKeyPath || !self.array.sectionValues.count) { + return nil; + } + id sectionValue = [self.array.sectionValues objectAtIndex:section]; + return [NSString stringWithFormat:@"%@", sectionValue]; } @end diff --git a/FirebaseUI/Implementation/FirebaseTableViewDataSource.m b/FirebaseUI/Implementation/FirebaseTableViewDataSource.m index d8cc1a896d2..65f551fe19f 100644 --- a/FirebaseUI/Implementation/FirebaseTableViewDataSource.m +++ b/FirebaseUI/Implementation/FirebaseTableViewDataSource.m @@ -39,6 +39,116 @@ @implementation FirebaseTableViewDataSource #pragma mark - #pragma mark FirebaseDataSource initializer methods +- (instancetype)initWithRef:(Firebase *)ref + sortDescriptors:(NSArray *)sortDescriptors + cellReuseIdentifier:(NSString *)identifier + view:(UITableView *)tableView { + return [self initWithRef:ref + sortDescriptors:sortDescriptors + modelClass:nil + cellClass:nil + cellReuseIdentifier:identifier + view:tableView]; +} + +-(instancetype)initWithRef:(Firebase *)ref + sortDescriptors:(NSArray *)sortDescriptors + prototypeReuseIdentifier:(NSString *)identifier + view:(UITableView *)tableView { + self.hasPrototypeCell = YES; + return [self initWithRef:ref + sortDescriptors:sortDescriptors + modelClass:nil + cellClass:nil + cellReuseIdentifier:identifier + view:tableView]; +} + +-(instancetype)initWithRef:(Firebase *)ref + sortDescriptors:(NSArray *)sortDescriptors + modelClass:(Class)model + cellReuseIdentifier:(NSString *)identifier + view:(UITableView *)tableView { + return [self initWithRef:ref + sortDescriptors:sortDescriptors + modelClass:model + cellClass:nil + cellReuseIdentifier:identifier + view:tableView]; +} + +-(instancetype)initWithRef:(Firebase *)ref + sortDescriptors:(NSArray *)sortDescriptors + nibNamed:(NSString *)nibName + cellReuseIdentifier:(NSString *)identifier + view:(UITableView *)tableView { + return [self initWithRef:ref + sortDescriptors:sortDescriptors + modelClass:nil + nibNamed:nibName + cellReuseIdentifier:identifier + view:tableView]; +} + +-(instancetype)initWithRef:(Firebase *)ref + sortDescriptors:(NSArray *)sortDescriptors + cellClass:(Class)cell + cellReuseIdentifier:(NSString *)identifier + view:(UITableView *)tableView { + return [self initWithRef:ref + sortDescriptors:sortDescriptors + modelClass:nil + cellClass:cell + cellReuseIdentifier:identifier + view:tableView]; + +} + +-(instancetype)initWithRef:(Firebase *)ref + sortDescriptors:(NSArray *)sortDescriptors + modelClass:(Class)model + prototypeReuseIdentifier:(NSString *)identifier + view:(UITableView *)tableView { + self.hasPrototypeCell = YES; + return [self initWithRef:ref + sortDescriptors:sortDescriptors + modelClass:model + cellClass:nil + cellReuseIdentifier:identifier + view:tableView]; +} + +-(instancetype)initWithRef:(Firebase *)ref + sortDescriptors:(NSArray *)sortDescriptors + modelClass:(Class)model + cellClass:(Class)cell + cellReuseIdentifier:(NSString *)identifier + view:(UITableView *)tableView { + return [self initWithRef:ref + predicate:nil + sortDescriptors:sortDescriptors + modelClass:model + cellClass:cell + cellReuseIdentifier:identifier + view:tableView]; +} + +-(instancetype)initWithRef:(Firebase *)ref + sortDescriptors:(NSArray *)sortDescriptors + modelClass:(Class)model + nibNamed:(NSString *)nibName + cellReuseIdentifier:(NSString *)identifier + view:(UITableView *)tableView { + + return [self initWithRef:ref + predicate:nil + sortDescriptors:sortDescriptors + modelClass:model + nibNamed:nibName + cellReuseIdentifier:identifier + view:tableView]; +} + - (instancetype)initWithRef:(Firebase *)ref cellReuseIdentifier:(NSString *)identifier view:(UITableView *)tableView { @@ -110,95 +220,170 @@ - (instancetype)initWithRef:(Firebase *)ref cellClass:(Class)cell cellReuseIdentifier:(NSString *)identifier view:(UITableView *)tableView { - FirebaseArray *array = [[FirebaseArray alloc] initWithRef:ref]; - self = [super initWithArray:array]; - if (self) { + return [self initWithRef:ref + sortDescriptors:nil + modelClass:model + cellClass:cell + cellReuseIdentifier:identifier + view:tableView]; +} + +- (instancetype)initWithRef:(Firebase *)ref + modelClass:(Class)model + nibNamed:(NSString *)nibName + cellReuseIdentifier:(NSString *)identifier + view:(UITableView *)tableView { + + return [self initWithRef:ref + sortDescriptors:nil + modelClass:model + nibNamed:nibName + cellReuseIdentifier:identifier + view:tableView]; +} + +-(instancetype)initWithRef:(Firebase *)ref + predicate:(NSPredicate *)predicate + sortDescriptors:(NSArray *)sortDescriptors + modelClass:(Class)model + prototypeReuseIdentifier:(NSString *)identifier + view:(UITableView *)tableView { + self.hasPrototypeCell = YES; + return [self initWithRef:ref + predicate:predicate + sortDescriptors:sortDescriptors + modelClass:model + cellClass:nil + cellReuseIdentifier:identifier + view:tableView]; +} + +-(instancetype)initWithRef:(Firebase *)ref + predicate:(NSPredicate *)predicate + sortDescriptors:(NSArray *)sortDescriptors + modelClass:(Class)model + cellClass:(Class)cell + cellReuseIdentifier:(NSString *)identifier + view:(UITableView *)tableView { + FirebaseArray *array = [[FirebaseArray alloc] initWithQuery:ref + sortDescriptors:sortDescriptors + predicate:predicate]; + self = [super initWithArray:array]; + if (!self) { + return nil; + } + if (!model) { - model = [FDataSnapshot class]; + model = [FDataSnapshot class]; } - + if (!cell) { - cell = [UITableViewCell class]; + cell = [UITableViewCell class]; } - + self.tableView = tableView; self.modelClass = model; self.reuseIdentifier = identifier; self.populateCell = ^(id cell, id object) { }; - + if (!self.hasPrototypeCell) { - [self.tableView registerClass:cell - forCellReuseIdentifier:self.reuseIdentifier]; + [self.tableView registerClass:cell + forCellReuseIdentifier:self.reuseIdentifier]; } - } - return self; + return self; } -- (instancetype)initWithRef:(Firebase *)ref - modelClass:(Class)model - nibNamed:(NSString *)nibName - cellReuseIdentifier:(NSString *)identifier - view:(UITableView *)tableView { - FirebaseArray *array = [[FirebaseArray alloc] initWithRef:ref]; - self = [super initWithArray:array]; - if (self) { +-(instancetype)initWithRef:(Firebase *)ref + predicate:(NSPredicate *)predicate + sortDescriptors:(NSArray *)sortDescriptors + modelClass:(Class)model + nibNamed:(NSString *)nibName + cellReuseIdentifier:(NSString *)identifier + view:(UITableView *)tableView { + FirebaseArray *array = [[FirebaseArray alloc] initWithQuery:ref + sortDescriptors:sortDescriptors + predicate:predicate]; + self = [super initWithArray:array]; + if (!self) { + return nil; + } + if (!model) { - model = [FDataSnapshot class]; + model = [FDataSnapshot class]; } - + self.tableView = tableView; self.modelClass = model; self.reuseIdentifier = identifier; self.populateCell = ^(id cell, id object) { }; - + if (!self.hasPrototypeCell) { - UINib *nib = [UINib nibWithNibName:nibName bundle:nil]; - [self.tableView registerNib:nib - forCellReuseIdentifier:self.reuseIdentifier]; + UINib *nib = [UINib nibWithNibName:nibName bundle:nil]; + [self.tableView registerNib:nib + forCellReuseIdentifier:self.reuseIdentifier]; } - } - return self; + return self; } #pragma mark - #pragma mark FirebaseCollectionDelegate methods -- (void)childAdded:(id)obj atIndex:(NSUInteger)index { +- (void)childAdded:(id)obj atIndexPath:(NSIndexPath *)indexPath { [self.tableView beginUpdates]; - [self.tableView insertRowsAtIndexPaths:@[ - [NSIndexPath indexPathForRow:index inSection:0] - ] withRowAnimation:UITableViewRowAnimationAutomatic]; + [self.tableView insertRowsAtIndexPaths:@[indexPath] + withRowAnimation:UITableViewRowAnimationAutomatic]; [self.tableView endUpdates]; } -- (void)childChanged:(id)obj atIndex:(NSUInteger)index { +- (void)childChanged:(id)obj atIndexPath:(NSIndexPath *)indexPath { [self.tableView beginUpdates]; - [self.tableView reloadRowsAtIndexPaths:@[ - [NSIndexPath indexPathForRow:index inSection:0] - ] withRowAnimation:UITableViewRowAnimationAutomatic]; + [self.tableView reloadRowsAtIndexPaths:@[indexPath] + withRowAnimation:UITableViewRowAnimationAutomatic]; [self.tableView endUpdates]; } -- (void)childRemoved:(id)obj atIndex:(NSUInteger)index { +- (void)childRemoved:(id)obj atIndexPath:(NSIndexPath *)indexPath { [self.tableView beginUpdates]; - [self.tableView deleteRowsAtIndexPaths:@[ - [NSIndexPath indexPathForRow:index inSection:0] - ] withRowAnimation:UITableViewRowAnimationAutomatic]; + [self.tableView deleteRowsAtIndexPaths:@[indexPath] + withRowAnimation:UITableViewRowAnimationAutomatic]; [self.tableView endUpdates]; } - (void)childMoved:(id)obj - fromIndex:(NSUInteger)fromIndex - toIndex:(NSUInteger)toIndex { + fromIndexPath:(NSIndexPath *)fromIndexPath + toIndexPath:(NSIndexPath *)toIndexPath { [self.tableView beginUpdates]; [self.tableView - moveRowAtIndexPath:[NSIndexPath indexPathForRow:fromIndex inSection:0] - toIndexPath:[NSIndexPath indexPathForRow:toIndex inSection:0]]; + moveRowAtIndexPath:fromIndexPath + toIndexPath:toIndexPath]; [self.tableView endUpdates]; } +-(void)sectionsAddedAtIndexes:(NSIndexSet *)indexes { + [self.tableView insertSections:indexes + withRowAnimation:UITableViewRowAnimationAutomatic]; +} + +- (void)sectionAddedAtSectionIndex:(NSUInteger)section { + [self.tableView insertSections:[NSIndexSet indexSetWithIndex:section] + withRowAnimation:UITableViewRowAnimationAutomatic]; +} + +- (void)sectionRemovedAtSectionIndex:(NSUInteger)section { + [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:section] + withRowAnimation:UITableViewRowAnimationAutomatic]; +} + +-(void)beginUpdates { + [self.tableView beginUpdates]; +} + +-(void)endUpdates { + [self.tableView endUpdates]; +} + #pragma mark - #pragma mark UITableViewDataSource methods @@ -208,7 +393,7 @@ - (id)tableView:(UITableView *)tableView [self.tableView dequeueReusableCellWithIdentifier:self.reuseIdentifier forIndexPath:indexPath]; - FDataSnapshot *snap = [self.array objectAtIndex:indexPath.row]; + FDataSnapshot *snap = [self.array objectAtIndexPath:indexPath]; if (![self.modelClass isSubclassOfClass:[FDataSnapshot class]]) { id model = [[self.modelClass alloc] init]; // TODO: replace setValuesForKeysWithDictionary with client API @@ -222,9 +407,17 @@ - (id)tableView:(UITableView *)tableView return cell; } +-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { + return [self sectionTitleForSection:section]; +} + - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return [self.array count]; + return [self.array sectionAtIndex:section].count; +} + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return self.array.numberOfSections; } - (void)populateCellWithBlock: diff --git a/examples/FirebaseUIChat/FirebaseUIChat/MessageTableViewCell.xib b/examples/FirebaseUIChat/FirebaseUIChat/MessageTableViewCell.xib index 594d661eed4..6690deff6c7 100644 --- a/examples/FirebaseUIChat/FirebaseUIChat/MessageTableViewCell.xib +++ b/examples/FirebaseUIChat/FirebaseUIChat/MessageTableViewCell.xib @@ -1,8 +1,8 @@ - + - + @@ -13,11 +13,11 @@ - + -