Skip to content
Browse files

Added support for grouping objects into sections within RKTableContro…

…ller. closes #658
  • Loading branch information...
1 parent b27be1c commit b282e3a81312abf26bea5ac79c2fb5fc63ad769e @blakewatters blakewatters committed
View
27 Code/Support/NSArray+RKAdditions.h
@@ -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
29 Code/Support/NSArray+RKAdditions.m
@@ -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
3 Code/Support/Support.h
@@ -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
12 Code/UI/RKTableController.h
@@ -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
41 Code/UI/RKTableController.m
@@ -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 @@ - (id)init {
- (void)dealloc {
[self removeObserver:self forKeyPath:@"sections"];
[_form release];
-
+ [_sectionNameKeyPath release];
+
[super dealloc];
}
@@ -93,10 +96,6 @@ - (void)addSection:(RKTableSection *)section {
}
}
-- (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 @@ - (NSArray*)objectsWithHeaderAndFooters:(NSArray *)objects forSection:(NSUIntege
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 @@ - (void)loadTableItems:(NSArray *)tableItems {
[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 @@ - (void)loadForm:(RKForm *)form {
[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)tableView:(UITableView*)theTableView moveRowAtIndexPath:(NSIndexPath *)s
- (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
18 RestKit.xcodeproj/project.pbxproj
@@ -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
30 Tests/Application/RKApplicationTests.xcodeproj/project.pbxproj
@@ -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
57 Tests/Application/UI/RKTableControllerTest.m
@@ -798,6 +798,32 @@ - (void)testDoSomethingWhenTheRequestLoadsAnUnexpectedResponse {
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 @@ - (void)testInvokeABlockCallbackWhenTheCellAccessoryButtonIsTapped {
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 @@ - (void)testInvokeABlockCallbackWhenTheDeleteConfirmationButtonTitleIsDetermined
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 @@ - (void)testInvokeABlockCallbackWhenCellEditingStyleIsDetermined {
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 @@ - (void)testInvokeABlockCallbackWhenACellIsMoved {
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 @@ - (void)testReturnTheRowHeightConfiguredOnTheTableViewWhenVariableHeightRowsIsDi
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 @@ - (void)testReturnTheHeightFromTheTableCellMappingWhenVariableHeightRowsAreEnabl
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 @@ - (void)testInvokeAnBlockCallbackToDetermineTheCellHeightWhenVariableHeightRowsA
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
59 Tests/Logic/Support/NSArray+RKAdditionsTest.m
@@ -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
11 Tests/Models/RKTestUser.m
@@ -36,10 +36,15 @@ + (RKTestUser*)user {
// 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 {

0 comments on commit b282e3a

Please sign in to comment.
Something went wrong with that request. Please try again.