Added support for grouping objects into sections within RKTableController. closes #658

This commit is contained in:
Blake Watters
2012-04-10 11:34:22 -04:00
parent b27be1cbc5
commit b282e3a813
10 changed files with 227 additions and 60 deletions

View File

@@ -0,0 +1,27 @@
//
// NSArray+RKAdditions.h
// RestKit
//
// Created by Blake Watters on 4/10/12.
// Copyright (c) 2012 RestKit. All rights reserved.
//
#import <Foundation/Foundation.h>
/**
Provides useful additions to the NSArray interface.
*/
@interface NSArray (RKAdditions)
/**
Evaluates a given key path against the receiving array, divides the array entries into
sections grouped by the value for the key path, and returns an aggregate array of arrays
containing the sections. The receiving array is assumed to be sorted.
@param keyPath The key path of the value to group the entries by.
@returns An array of section arrays, with each section containing a group of objects sharing
the same value for the given key path.
*/
- (NSArray *)sectionsGroupedByKeyPath:(NSString *)keyPath;
@end

View File

@@ -0,0 +1,29 @@
//
// NSArray+RKAdditions.m
// RestKit
//
// Created by Blake Watters on 4/10/12.
// Copyright (c) 2012 RestKit. All rights reserved.
//
#import "NSArray+RKAdditions.h"
@implementation NSArray (RKAdditions)
- (NSArray *)sectionsGroupedByKeyPath:(NSString *)keyPath
{
NSString *keyPathWithOperator = [NSString stringWithFormat:@"@distinctUnionOfObjects.%@", keyPath];
NSArray *sectionValues = [self valueForKeyPath:keyPathWithOperator];
NSMutableArray *sections = [NSMutableArray arrayWithCapacity:[sectionValues count]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K = $sectionValue", keyPath];
for (id value in sectionValues) {
NSDictionary *sub = [NSDictionary dictionaryWithObject:value forKey:@"sectionValue"];
NSPredicate *sectionPredicate = [predicate predicateWithSubstitutionVariables:sub];
NSArray *objectsForSection = [self filteredArrayUsingPredicate:sectionPredicate];
[sections addObject:objectsForSection];
}
return [NSArray arrayWithArray:sections];
}
@end

View File

@@ -30,3 +30,6 @@
#import "NSDictionary+RKAdditions.h"
#import "NSString+RKAdditions.h"
#import "NSBundle+RKAdditions.h"
#import "NSArray+RKAdditions.h"
#import "NSData+RKAdditions.h"
#import "NSURL+RKAdditions.h"

View File

@@ -48,7 +48,6 @@
After the block is invoked, the objects will be loaded into the specified section.
*/
// TODO: Update comments...
- (void)loadTableItems:(NSArray *)tableItems withMappingBlock:(void (^)(RKTableViewCellMapping *))block; // TODO: Eliminate...
- (void)loadTableItems:(NSArray *)tableItems withMapping:(RKTableViewCellMapping *)cellMapping;
- (void)loadTableItems:(NSArray *)tableItems
inSection:(NSUInteger)sectionIndex
@@ -103,6 +102,11 @@
/// @name Managing Sections
/////////////////////////////////////////////////////////////////////////
/**
The key path on the loaded objects used to determine the section they belong to.
*/
@property(nonatomic, copy) NSString *sectionNameKeyPath;
// Coalesces a series of table view updates performed within the block into
// a single animation using beginUpdates: and endUpdates: on the table view
// TODO: Move to super-class?
@@ -113,12 +117,6 @@
// NOTE: connects cellMappings if section.cellMappings is nil...
- (void)addSection:(RKTableSection *)section;
/**
Creates an section and yields it to the block for configuration. After the block
is evaluated, the section is added to the table.
*/
- (void)addSectionUsingBlock:(void (^)(RKTableSection *section))block;
/** Inserts a new section at the specified index.
* @param section Must be a valid non nil RKTableViewSection.
* @param index Must be less than the total number of sections. */

View File

@@ -22,6 +22,7 @@
#import "RKAbstractTableController_Internals.h"
#import "RKLog.h"
#import "RKFormSection.h"
#import "NSArray+RKAdditions.h"
// Define logging component
#undef RKLogComponent
@@ -30,6 +31,7 @@
@implementation RKTableController
@synthesize form = _form;
@synthesize sectionNameKeyPath = _sectionNameKeyPath;
#pragma mark - Instantiation
@@ -51,7 +53,8 @@
- (void)dealloc {
[self removeObserver:self forKeyPath:@"sections"];
[_form release];
[_sectionNameKeyPath release];
[super dealloc];
}
@@ -93,10 +96,6 @@
}
}
- (void)addSectionUsingBlock:(void (^)(RKTableSection *section))block {
[self addSection:[RKTableSection sectionUsingBlock:block]];
}
- (void)removeSection:(RKTableSection *)section {
NSAssert(section, @"Cannot remove a nil section");
if ([self.sections containsObject:section] && self.sectionCount == 1) {
@@ -179,7 +178,7 @@
return [mutableObjects autorelease];
}
// TODO: NOTE - Everything currently needs to pass through this method to pick up header/footer rows...
// NOTE - Everything currently needs to pass through this method to pick up header/footer rows...
- (void)loadObjects:(NSArray *)objects inSection:(NSUInteger)sectionIndex {
// Clear any existing error state from the table
self.error = nil;
@@ -241,10 +240,6 @@
[self loadTableItems:tableItems inSection:0];
}
- (void)loadTableItems:(NSArray *)tableItems withMappingBlock:(void (^)(RKTableViewCellMapping*))block {
[self loadTableItems:tableItems inSection:0 withMapping:[RKTableViewCellMapping cellMappingUsingBlock:block]];
}
#pragma mark - Network Table Loading
- (void)loadTableFromResourcePath:(NSString*)resourcePath {
@@ -275,12 +270,6 @@
[self addSection:(RKTableSection *)section];
}
// TODO: How to handle animating loading a replacement form?
// if (self.loaded) {
// NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [form.sections count] - 1)];
// [self.tableView reloadSections:indexSet withRowAnimation:self.defaultRowAnimation];
// }
[self didFinishLoad];
[form didLoadInTableController:self];
}
@@ -331,15 +320,25 @@
- (void)objectLoader:(RKObjectLoader *)loader didLoadObjects:(NSArray *)objects {
// TODO: Could not get the KVO to work without a boolean property...
// TODO: Need to figure out how to group the objects into sections
// TODO: Apply any sorting...
// Load them into the first section for now
[self loadObjects:objects inSection:0];
if (self.sectionNameKeyPath) {
[self removeAllSections];
NSArray *sectionedObjects = [objects sectionsGroupedByKeyPath:self.sectionNameKeyPath];
for (NSArray *sectionOfObjects in sectionedObjects) {
NSUInteger sectionIndex = [sectionedObjects indexOfObject:sectionOfObjects];
if (sectionIndex >= [self sectionCount]) {
[self addSection:[RKTableSection section]];
}
[self loadObjects:sectionOfObjects inSection:sectionIndex];
}
} else {
[self loadObjects:objects inSection:0];
}
}
- (void)reloadRowForObject:(id)object withRowAnimation:(UITableViewRowAnimation)rowAnimation {
// TODO: Find the indexPath of the object...
NSIndexPath *indexPath = [self indexPathForObject:object];
if (indexPath) {
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:rowAnimation];

View File

@@ -472,6 +472,12 @@
2516112E1456F5520060A5C5 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2516112D1456F5520060A5C5 /* CoreData.framework */; };
251611301456F5590060A5C5 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2516112F1456F5590060A5C5 /* Security.framework */; };
251611321456F56C0060A5C5 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 251611311456F56C0060A5C5 /* MobileCoreServices.framework */; };
252A202D153471380078F8AD /* NSArray+RKAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 252A202B153471380078F8AD /* NSArray+RKAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
252A202E153471380078F8AD /* NSArray+RKAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 252A202C153471380078F8AD /* NSArray+RKAdditions.m */; };
252A2030153471470078F8AD /* NSArray+RKAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 252A202C153471380078F8AD /* NSArray+RKAdditions.m */; };
252A20311534714D0078F8AD /* NSArray+RKAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 252A202B153471380078F8AD /* NSArray+RKAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
252A2034153477870078F8AD /* NSArray+RKAdditionsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 252A2033153477870078F8AD /* NSArray+RKAdditionsTest.m */; };
252A2035153477870078F8AD /* NSArray+RKAdditionsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 252A2033153477870078F8AD /* NSArray+RKAdditionsTest.m */; };
252EFAFA14D8EAEC004863C8 /* RKEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 252EFAF914D8EAEC004863C8 /* RKEvent.m */; };
252EFAFB14D8EAEC004863C8 /* RKEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 252EFAF914D8EAEC004863C8 /* RKEvent.m */; };
252EFB0814D98F4D004863C8 /* RKSearchableManagedObjectTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 252EFB0614D98F4D004863C8 /* RKSearchableManagedObjectTest.m */; };
@@ -1007,6 +1013,9 @@
2516112D1456F5520060A5C5 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
2516112F1456F5590060A5C5 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
251611311456F56C0060A5C5 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
252A202B153471380078F8AD /* NSArray+RKAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+RKAdditions.h"; sourceTree = "<group>"; };
252A202C153471380078F8AD /* NSArray+RKAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+RKAdditions.m"; sourceTree = "<group>"; };
252A2033153477870078F8AD /* NSArray+RKAdditionsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+RKAdditionsTest.m"; sourceTree = "<group>"; };
252EFAF814D8EAEC004863C8 /* RKEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKEvent.h; sourceTree = "<group>"; };
252EFAF914D8EAEC004863C8 /* RKEvent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKEvent.m; sourceTree = "<group>"; };
252EFAFC14D8EB30004863C8 /* RKTestNotificationObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RKTestNotificationObserver.h; path = Testing/RKTestNotificationObserver.h; sourceTree = "<group>"; };
@@ -1495,6 +1504,8 @@
25B6E95714CF7A1C00B1E881 /* RKErrors.m */,
252EFB1914D9A7CB004863C8 /* NSBundle+RKAdditions.h */,
252EFB1A14D9A7CB004863C8 /* NSBundle+RKAdditions.m */,
252A202B153471380078F8AD /* NSArray+RKAdditions.h */,
252A202C153471380078F8AD /* NSArray+RKAdditions.m */,
);
path = Support;
sourceTree = "<group>";
@@ -1891,6 +1902,7 @@
251610551456F2330060A5C5 /* RKJSONParserJSONKitTest.m */,
251610561456F2330060A5C5 /* RKPathMatcherTest.m */,
251610571456F2330060A5C5 /* RKXMLParserTest.m */,
252A2033153477870078F8AD /* NSArray+RKAdditionsTest.m */,
);
name = Support;
path = Logic/Support;
@@ -2198,6 +2210,7 @@
257ABAB015112DD500CCAA76 /* NSManagedObjectContext+RKAdditions.h in Headers */,
257ABAB61511371E00CCAA76 /* NSManagedObject+RKAdditions.h in Headers */,
25079C6F151B93DB00266AE7 /* NSEntityDescription+RKAdditions.h in Headers */,
252A202D153471380078F8AD /* NSArray+RKAdditions.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2318,6 +2331,7 @@
257ABAB115112DD500CCAA76 /* NSManagedObjectContext+RKAdditions.h in Headers */,
257ABAB71511371E00CCAA76 /* NSManagedObject+RKAdditions.h in Headers */,
25079C70151B93DB00266AE7 /* NSEntityDescription+RKAdditions.h in Headers */,
252A20311534714D0078F8AD /* NSArray+RKAdditions.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2717,6 +2731,7 @@
257ABAB215112DD500CCAA76 /* NSManagedObjectContext+RKAdditions.m in Sources */,
257ABAB81511371E00CCAA76 /* NSManagedObject+RKAdditions.m in Sources */,
25079C71151B93DB00266AE7 /* NSEntityDescription+RKAdditions.m in Sources */,
252A202E153471380078F8AD /* NSArray+RKAdditions.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2800,6 +2815,7 @@
25E36E0215195CED00F9E448 /* RKFetchRequestMappingCacheTest.m in Sources */,
25079C76151B952200266AE7 /* NSEntityDescription+RKAdditionsTest.m in Sources */,
25DB7508151BD551009F01AF /* NSManagedObject+ActiveRecordTest.m in Sources */,
252A2034153477870078F8AD /* NSArray+RKAdditionsTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2897,6 +2913,7 @@
257ABAB91511371E00CCAA76 /* NSManagedObject+RKAdditions.m in Sources */,
25079C72151B93DB00266AE7 /* NSEntityDescription+RKAdditions.m in Sources */,
250B849E152B6F63002581F9 /* RKObjectMappingProvider+CoreData.m in Sources */,
252A2030153471470078F8AD /* NSArray+RKAdditions.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2980,6 +2997,7 @@
25E36E0315195CED00F9E448 /* RKFetchRequestMappingCacheTest.m in Sources */,
25079C77151B952200266AE7 /* NSEntityDescription+RKAdditionsTest.m in Sources */,
25DB7509151BD551009F01AF /* NSManagedObject+ActiveRecordTest.m in Sources */,
252A2035153477870078F8AD /* NSArray+RKAdditionsTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -111,6 +111,13 @@
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
252A204215347F0B0078F8AD /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 253B489814E341C500B0483F /* RestKit.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 259C301615128079003066A2;
remoteInfo = RestKitResources;
};
253B488514E3415800B0483F /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 253B485614E3415800B0483F /* Project object */;
@@ -153,13 +160,6 @@
remoteGlobalIDString = 25160D1514564E810060A5C5;
remoteInfo = RestKit;
};
25A41055152F23990098D0D0 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 253B489814E341C500B0483F /* RestKit.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 259C301615128079003066A2;
remoteInfo = RestKitResources;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
@@ -434,7 +434,7 @@
253B48A514E341C500B0483F /* RestKitTests.octest */,
253B48A714E341C500B0483F /* RestKit.framework */,
253B48A914E341C500B0483F /* RestKitFrameworkTests.octest */,
25A41056152F23990098D0D0 /* RestKitResources.bundle */,
252A204315347F0B0078F8AD /* RestKitResources.bundle */,
);
name = Products;
sourceTree = "<group>";
@@ -734,6 +734,13 @@
/* End PBXProject section */
/* Begin PBXReferenceProxy section */
252A204315347F0B0078F8AD /* RestKitResources.bundle */ = {
isa = PBXReferenceProxy;
fileType = wrapper.cfbundle;
path = RestKitResources.bundle;
remoteRef = 252A204215347F0B0078F8AD /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
253B48A314E341C500B0483F /* libRestKit.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
@@ -762,13 +769,6 @@
remoteRef = 253B48A814E341C500B0483F /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
25A41056152F23990098D0D0 /* RestKitResources.bundle */ = {
isa = PBXReferenceProxy;
fileType = wrapper.cfbundle;
path = RestKitResources.bundle;
remoteRef = 25A41055152F23990098D0D0 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */

View File

@@ -798,6 +798,32 @@
RKLogCritical(@"PENDING - Undefined Behavior!!!");
}
- (void)testLoadCollectionOfObjectsAndMapThemIntoSections {
RKObjectManager* objectManager = [RKTestFactory objectManager];
objectManager.client.cachePolicy = RKRequestCachePolicyNone;
RKTableControllerTestTableViewController* viewController = [RKTableControllerTestTableViewController new];
RKTableController* tableController = [RKTableController tableControllerForTableViewController:viewController];
tableController.objectManager = objectManager;
[tableController mapObjectsWithClass:[RKTestUser class] toTableCellsWithMapping:[RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping* mapping) {
mapping.cellClass = [RKTestUserTableViewCell class];
[mapping mapKeyPath:@"name" toAttribute:@"textLabel.text"];
[mapping mapKeyPath:@"nickName" toAttribute:@"detailTextLabel.text"];
}]];
tableController.sectionNameKeyPath = @"name";
RKTestTableControllerDelegate* delegate = [RKTestTableControllerDelegate tableControllerDelegate];
delegate.timeout = 10;
tableController.delegate = delegate;
[tableController loadTableFromResourcePath:@"/JSON/users.json" usingBlock:^(RKObjectLoader* objectLoader) {
objectLoader.objectMapping = [RKObjectMapping mappingForClass:[RKTestUser class] usingBlock:^(RKObjectMapping* mapping) {
[mapping mapAttributes:@"name", nil];
}];
}];
[delegate waitForLoad];
assertThatBool([tableController isLoaded], is(equalToBool(YES)));
assertThatInt(tableController.sectionCount, is(equalToInt(3)));
assertThatInt(tableController.rowCount, is(equalToInt(3)));
}
#pragma mark - RKTableViewDelegate specs
- (void)testNotifyTheDelegateWhenLoadingStarts {
@@ -1594,11 +1620,12 @@
RKTableController* tableController = [RKTableController tableControllerForTableViewController:viewController];
RKTableItem* tableItem = [RKTableItem tableItem];
__block BOOL dispatched = NO;
[tableController loadTableItems:[NSArray arrayWithObject:tableItem] withMappingBlock:^(RKTableViewCellMapping* cellMapping) {
RKTableViewCellMapping *mapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) {
cellMapping.onTapAccessoryButtonForObjectAtIndexPath = ^(UITableViewCell* cell, id object, NSIndexPath* indexPath) {
dispatched = YES;
};
}];
[tableController loadTableItems:[NSArray arrayWithObject:tableItem] withMapping:mapping];
[tableController tableView:tableController.tableView accessoryButtonTappedForRowWithIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
assertThatBool(dispatched, is(equalToBool(YES)));
}
@@ -1608,11 +1635,12 @@
RKTableController* tableController = [RKTableController tableControllerForTableViewController:viewController];
RKTableItem* tableItem = [RKTableItem tableItem];
NSString* deleteTitle = @"Delete Me";
[tableController loadTableItems:[NSArray arrayWithObject:tableItem] withMappingBlock:^(RKTableViewCellMapping* cellMapping) {
RKTableViewCellMapping *mapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) {
cellMapping.titleForDeleteButtonForObjectAtIndexPath = ^ NSString*(UITableViewCell* cell, id object, NSIndexPath* indexPath) {
return deleteTitle;
};
}];
}];
[tableController loadTableItems:[NSArray arrayWithObject:tableItem] withMapping:mapping];
NSString* delegateTitle = [tableController tableView:tableController.tableView
titleForDeleteConfirmationButtonForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
assertThat(delegateTitle, is(equalTo(deleteTitle)));
@@ -1623,11 +1651,12 @@
RKTableController* tableController = [RKTableController tableControllerForTableViewController:viewController];
tableController.canEditRows = YES;
RKTableItem* tableItem = [RKTableItem tableItem];
[tableController loadTableItems:[NSArray arrayWithObject:tableItem] withMappingBlock:^(RKTableViewCellMapping* cellMapping) {
RKTableViewCellMapping *mapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) {
cellMapping.editingStyleForObjectAtIndexPath = ^ UITableViewCellEditingStyle(UITableViewCell* cell, id object, NSIndexPath* indexPath) {
return UITableViewCellEditingStyleInsert;
};
}];
}];
[tableController loadTableItems:[NSArray arrayWithObject:tableItem] withMapping:mapping];
UITableViewCellEditingStyle delegateStyle = [tableController tableView:tableController.tableView
editingStyleForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
assertThatInt(delegateStyle, is(equalToInt(UITableViewCellEditingStyleInsert)));
@@ -1639,11 +1668,11 @@
tableController.canMoveRows = YES;
RKTableItem* tableItem = [RKTableItem tableItem];
NSIndexPath* moveToIndexPath = [NSIndexPath indexPathForRow:2 inSection:0];
[tableController loadTableItems:[NSArray arrayWithObject:tableItem] withMappingBlock:^(RKTableViewCellMapping* cellMapping) {
[tableController loadTableItems:[NSArray arrayWithObject:tableItem] withMapping:[RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping* cellMapping) {
cellMapping.targetIndexPathForMove = ^ NSIndexPath*(UITableViewCell* cell, id object, NSIndexPath* sourceIndexPath, NSIndexPath* destinationIndexPath) {
return moveToIndexPath;
};
}];
}]];
NSIndexPath* delegateIndexPath = [tableController tableView:tableController.tableView
targetIndexPathForMoveFromRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]toProposedIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]];
assertThat(delegateIndexPath, is(equalTo(moveToIndexPath)));
@@ -1657,9 +1686,9 @@
tableController.variableHeightRows = NO;
tableController.tableView.rowHeight = 55;
RKTableItem* tableItem = [RKTableItem tableItem];
[tableController loadTableItems:[NSArray arrayWithObject:tableItem] withMappingBlock:^(RKTableViewCellMapping* cellMapping) {
[tableController loadTableItems:[NSArray arrayWithObject:tableItem] withMapping:[RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping* cellMapping) {
cellMapping.rowHeight = 200;
}];
}]];
CGFloat height = [tableController tableView:tableController.tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
assertThatFloat(height, is(equalToFloat(55)));
}
@@ -1670,9 +1699,9 @@
tableController.variableHeightRows = YES;
tableController.tableView.rowHeight = 55;
RKTableItem* tableItem = [RKTableItem tableItem];
[tableController loadTableItems:[NSArray arrayWithObject:tableItem] withMappingBlock:^(RKTableViewCellMapping* cellMapping) {
cellMapping.rowHeight = 200;
}];
[tableController loadTableItems:[NSArray arrayWithObject:tableItem] withMapping:[RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping* cellMapping) {
cellMapping.rowHeight = 200;
}]];
CGFloat height = [tableController tableView:tableController.tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
assertThatFloat(height, is(equalToFloat(200)));
}
@@ -1683,10 +1712,10 @@
tableController.variableHeightRows = YES;
tableController.tableView.rowHeight = 55;
RKTableItem* tableItem = [RKTableItem tableItem];
[tableController loadTableItems:[NSArray arrayWithObject:tableItem] withMappingBlock:^(RKTableViewCellMapping* cellMapping) {
[tableController loadTableItems:[NSArray arrayWithObject:tableItem] withMapping:[RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping* cellMapping) {
cellMapping.rowHeight = 200;
cellMapping.heightOfCellForObjectAtIndexPath = ^ CGFloat(id object, NSIndexPath* indexPath) { return 150; };
}];
}]];
CGFloat height = [tableController tableView:tableController.tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
assertThatFloat(height, is(equalToFloat(150)));
}

View File

@@ -0,0 +1,59 @@
//
// NSArray+RKAdditionsTest.m
// RestKit
//
// Created by Blake Watters on 4/10/12.
// Copyright (c) 2012 RestKit. All rights reserved.
//
#import "RKTestEnvironment.h"
#import "NSArray+RKAdditions.h"
#import "RKTestUser.h"
@interface NSArray_RKAdditionsTest : RKTestCase
@end
@implementation NSArray_RKAdditionsTest
#pragma mark - sectionsGroupedByKeyPath Tests
- (void)testReturnsEmptyArrayWhenGroupingEmptyArray
{
NSArray *objects = [NSArray array];
assertThat([objects sectionsGroupedByKeyPath:@"whatever"], is(empty()));
}
- (void)testReturnsSingleSectionWhenGroupingSingleObject
{
RKTestUser *user = [RKTestUser new];
user.name = @"Blake";
user.country = @"USA";
NSArray *users = [NSArray arrayWithObject:user];
NSArray *sections = [users sectionsGroupedByKeyPath:@"country"];
assertThat(sections, hasCountOf(1));
}
- (void)testReturnsTwoSectionsWhenGroupingThreeObjectsWithTwoUniqueValues
{
RKTestUser *user1 = [RKTestUser new];
user1.name = @"Blake";
user1.country = @"USA";
RKTestUser *user2 = [RKTestUser new];
user2.name = @"Colin";
user2.country = @"USA";
RKTestUser *user3 = [RKTestUser new];
user3.name = @"Pepe";
user3.country = @"Spain";
NSArray *users = [NSArray arrayWithObjects:user1, user2, user3, nil];
NSArray *sections = [users sectionsGroupedByKeyPath:@"country"];
assertThat(sections, hasCountOf(2));
assertThat([sections objectAtIndex:0], contains(user1, user2, nil));
assertThat([sections objectAtIndex:1], contains(user3, nil));
}
@end

View File

@@ -36,10 +36,15 @@
// to determine if assocation values should be set
- (BOOL)isEqual:(id)object {
if ([object isKindOfClass:[RKTestUser class]]) {
return [[(RKTestUser*)object userID] isEqualToNumber:self.userID];
} else {
return NO;
if ([(RKTestUser*)object userID] == nil && self.userID == nil) {
// No primary key -- consult superclass
return [super isEqual:object];
} else {
return [[(RKTestUser*)object userID] isEqualToNumber:self.userID];
}
}
return NO;
}
- (id)valueForUndefinedKey:(NSString *)key {