My Bronze and Silver solutions using BNRItemStore


#1

My original idea was to make the sorting happen in BNRItemsViewController but something in the text caught my eye: the BNRItemsViewController asks the BNRItemStore for the array that contains all the items. If a subset of the items is desired, why couldn’t BNRItemStore know how to return that instead? Since BNRItemStore is what manipulates the items - adding, reordering, etc. - I put the method to return certain types of items (in this case, greater than or less than $50…) in the BNRItemStore…

BNRItemStore.h

[code]@property (nonatomic, readonly) NSArray *allItems;

// Notice that this a class method and prefixed with a + instead of a -

  • (instancetype)sharedStore;
  • (BNRItem *)createItem;
    // New methods to return certain items - probably should have made these @property’s
  • (NSArray *)itemsOver50;
  • (NSArray *)itemsUnder50;

@end[/code]

Two new methods in BNRItemStore.m that return a subset of allItems:

[code]- (NSArray *)itemsOver50
{
// Fill with BNRItems based on value > 50
NSMutableArray *holdingArray = [[NSMutableArray alloc] init];
for (BNRItem *item in self.privateItems) {
if (item.valueInDollars > 50) {
[holdingArray addObject:item];
}
}
// No more items!
[holdingArray addObject:@“No more items”];

return holdingArray;

}

  • (NSArray *)itemsUnder50
    {
    // Fill with BNRItems based on value < 50
    NSMutableArray *holdingArray = [[NSMutableArray alloc] init];
    for (BNRItem *item in self.privateItems) {
    if (item.valueInDollars < 50) {
    [holdingArray addObject:item];
    }
    }
    // No more items!
    [holdingArray addObject:@“No more items”];

    return holdingArray;
    }[/code]

In the BNRItemsViewController.m I made an array to hold the various sections:

[code]- (NSArray *)BNRDisplayStore
{
// BNRDisplayArray asks for two arrays from BNRItemStore
// One with items under 50 and one with items over 50
NSMutableArray *_temp = [[NSMutableArray alloc] init];
[_temp addObject:[[BNRItemStore sharedStore] itemsUnder50]];
[_temp addObject:[[BNRItemStore sharedStore] itemsOver50]];

return _temp;

}[/code]

Used that array to determine how many sections there are:

[code]- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{

// Count the number of arrays in BNRDisplayStore to determine how many sections to create
NSArray *itemSection = self.BNRDisplayStore[section];
return [itemSection count];

}

  • (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
    {
    // Count the number of arrays used to sort the items
    return [self.BNRDisplayStore count];
    }[/code]

And then populated the cells:

[code]- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@“UITableViewCell” forIndexPath:indexPath];

// Set the text on the cell with the description of the item
// First determine which array to check based on the section
// Then determine which item based on the row
NSArray *itemSection = self.BNRDisplayStore[indexPath.section];
BNRItem *item = itemSection[indexPath.row];
cell.textLabel.text = [item description];

return cell;

}[/code]

I originally did the sorting completely within BNRItemsViewController.m and I’m not sure which is best in terms of extendability. If the BNRItemsStore implements new sorting or filtering methods, then it can be used elsewhere. The other approach, that the ItemsStore always returns allItems and the sorting, filtering, etc. is done within the ViewController could work as well…


#2

Hi!

I wrote my solution based on yours. However, my app crashes at launch.

By playing around with the debugging tools, I am now fairly sure that the app crashed at the return holdingArray; part of

[code]-(NSArray *)overTwoThousand
{
NSMutableArray *holdingArray = [[NSMutableArray alloc]init];
for (BNRItem *item in self.privateItems) {
if (item.value > 2000) {
[holdingArray addObject:item];
}
}

return holdingArray;

}[/code]

By setting breakpoints, I saw that the holdingArray array is indeed populated by the items for which value > 2000. Then, after checking the 5 items, it crashes with error:

[quote]*** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil’
*** First throw call stack:
(
0 CoreFoundation 0x017f01e4 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x0156f8e5 objc_exception_throw + 44
2 CoreFoundation 0x017a2abc -[__NSArrayM insertObject:atIndex:] + 844
3 CoreFoundation 0x017a2760 -[__NSArrayM addObject:] + 64
4 Homepwner 0x0000444b -[BNRItemViewController displaySections] + 315
5 Homepwner 0x00003f7e -[BNRItemViewController numberOfSectionsInTableView:] + 78
6 UIKit 0x00525c0a -[_UIFilteredDataSource numberOfSectionsInTableView:] + 122
7 UIKit 0x00496712 -[UITableViewRowData(UITableViewRowDataPrivate) _updateNumSections] + 102
8 UIKit 0x00497513 -[UITableViewRowData invalidateAllSections] + 69
9 UIKit 0x003026ea -[UITableView _updateRowData] + 197
10 UIKit 0x00302620 -[UITableView _ensureRowDataIsLoaded] + 45
11 UIKit 0x003164f3 -[UITableView numberOfSections] + 35
12 UIKit 0x005033f7 -[UITableViewController viewWillAppear:] + 103
13 UIKit 0x0035204b -[UIViewController _setViewAppearState:isAnimating:] + 448
14 UIKit 0x00352548 -[UIViewController __viewWillAppear:] + 114
15 UIKit 0x00353507 -[UIViewController viewWillMoveToWindow:] + 387
16 UIKit 0x0028f78d -[UIView(Hierarchy) _willMoveToWindow:withAncestorView:] + 619
17 UIKit 0x0029b849 -[UIView(Internal) _addSubview:positioned:relativeTo:] + 456
18 UIKit 0x0028edba -[UIView(Hierarchy) addSubview:] + 56
19 UIKit 0x0026e406 -[UIWindow addRootViewControllerViewIfPossible] + 481
20 UIKit 0x0026e5ef -[UIWindow _setHidden:forced:] + 312
21 UIKit 0x0026e86b -[UIWindow _orderFrontWithoutMakingKey] + 49
22 UIKit 0x002793c8 -[UIWindow makeKeyAndVisible] + 65
23 Homepwner 0x0000399b -[BNRAppDelegate application:didFinishLaunchingWithOptions:] + 683
24 UIKit 0x0022914f -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 309
25 UIKit 0x00229aa1 -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1810
26 UIKit 0x0022e667 -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 824
27 UIKit 0x00242f92 -[UIApplication handleEvent:withNewEvent:] + 3517
28 UIKit 0x00243555 -[UIApplication sendEvent:] + 85
29 UIKit 0x00230250 _UIApplicationHandleEvent + 683
30 GraphicsServices 0x037e5f02 _PurpleEventCallback + 776
31 GraphicsServices 0x037e5a0d PurpleEventCallback + 46
32 CoreFoundation 0x0176bca5 CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION + 53
33 CoreFoundation 0x0176b9db __CFRunLoopDoSource1 + 523
34 CoreFoundation 0x0179668c __CFRunLoopRun + 2156
35 CoreFoundation 0x017959d3 CFRunLoopRunSpecific + 467
36 CoreFoundation 0x017957eb CFRunLoopRunInMode + 123
37 UIKit 0x0022dd9c -[UIApplication _run] + 840
38 UIKit 0x0022ff9b UIApplicationMain + 1225
39 Homepwner 0x000029ed main + 141
40 libdyld.dylib 0x01e37701 start + 1
41 ??? 0x00000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
[/quote]

Being rather inexperienced, I am not sure now if the problem really is located where I think.

Could anyone please confirm and maybe help out? I’m really confused with this chapter… :blush:

Thanks a lot!

NC


#3

Do you have a method called -displaySections in BNRItemViewController?

If I’m reading the dump correctly, it’s trying to show a nil item in the table view.

If I were to guess (given that very little code was provided), I’d guess that when the UITableView asked for how many rows were present in a given section, it was told the wrong number.


#4

I am not sure what went wrong. I deleted everything, wrote it from scratch and it worked. :slight_smile:
Thanks for having a look!