My Bronze, Silver and Gold challenge

#1

This is my solution. I kept everything from the challenges in chapter 9, so it’s a neat little app that can add new items to the various section, starting with no items if you do not initialize 5 random items at the beginning. Also, you cannot change sections (As an item < $50 will always be < $50. At least for now.). It also has a UINavigation controller, since otherwise, the top of the table will clash with the top section of the phone (If that makes any sense.). If you guys have any suggestions to make my code better, definitely let me know!

In AppDelegate.m

[code]#import “AppDelegate.h”
#import “BNRItemsViewController.h”

@interface AppDelegate ()

@end

@implementation AppDelegate

  • (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.

    BNRItemsViewController *itemsController = [[BNRItemsViewController alloc]init];

    UINavigationController *navigationController = [[UINavigationController alloc]initWithRootViewController:itemsController];
    itemsController.title = @“Homepwner”;
    self.window.rootViewController = navigationController;

    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
    }
    [/code]

BNRItem.h

[code] #import <Foundation/Foundation.h>

@interface BNRItem : NSObject

@property (nonatomic, copy) NSString *itemName;
@property (nonatomic, copy) NSString *serialNumber;
@property (nonatomic) int valueInDollars;
@property (nonatomic, readonly, strong) NSDate *dateCreated;

+(instancetype)randomItem;

-(instancetype)initWithItemName:(NSString *)name
valueInDollars:(int)value
serialNumber:(NSString *)serialNumber;

-(instancetype)initWithItemName:(NSString *)name;

@end
[/code]

BNRItem.m

[code] #import “BNRItem.h”

@implementation BNRItem

+(instancetype)randomItem
{
//Make 2 arrays, one for the random names to choose from, one for the adjectives
NSArray *randomAdjectiveList =
@[@“Fluffy”, @“Rusty”, @“Shiny”];
NSArray *randomNounList =@[@“Bear”, @“Spork”, @“Mac”];

//This will choose a random number between 0 and the value after the %
NSInteger adjectiveIndex = arc4random() % [randomAdjectiveList count];
NSInteger nounIndex = arc4random() % [randomNounList count];

//Choose the random name in the name and adjective arrays at the random indexes
NSString *randomName = [NSString stringWithFormat:@"%@ %@", randomAdjectiveList[adjectiveIndex],
                        randomNounList[nounIndex]];

//A random value between 0 and 100
int randomValue = arc4random() % 100;

//A random serial number in the format ANANAN
NSString *randomSerialNumber = [NSString stringWithFormat:@"%c%c%c%c%c%c",
                                'A' + arc4random() % 26,
                                '0' + arc4random() % 10,
                                'A' + arc4random() % 26,
                                '0' + arc4random() % 10,
                                'A' + arc4random() % 26,
                                '0' + arc4random() % 10];

/*Construct the random item with the above-generated random values. Note that self refers to the class, not an instace variable*/
BNRItem *newItem = [[self alloc]initWithItemName:randomName
                               valueInDollars:randomValue
                                 serialNumber:randomSerialNumber];

return newItem;

}

-(instancetype)initWithItemName:(NSString *)name
valueInDollars:(int)value
serialNumber:(NSString *)serialNumber
{
self = [super init];

if (self)
{
    _itemName = name;
    _valueInDollars = value;
    _serialNumber = serialNumber;
    //Set _dateCreated to the current date and time
    _dateCreated = [[NSDate alloc]init];
}

return self;

}

-(instancetype)initWithItemName:(NSString *)name
{
return [self initWithItemName:name
valueInDollars:0
serialNumber:@""];
}

-(instancetype)init
{
return [self initWithItemName:@“Item”];
}

-(NSString *)description
{
NSString *descriptionString = [[NSString alloc] initWithFormat:@"%@ (%@): Worth $%d recorded on %@", self.itemName, self.serialNumber, self.valueInDollars, self.dateCreated];

return descriptionString;

}

@end
[/code]

BNRItemsViewController.h

[code] #import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface BNRItemsViewController : UITableViewController

-(instancetype)init;

-(instancetype)initWithStyle:(UITableViewStyle)style;

@end
[/code]

BNRItemsViewController.m


#import "BNRItemsViewController.h"
#import "BNRItem.h"
#import "BNRItemStore.h"

@interface BNRItemsViewController ()

@property (nonatomic, strong) IBOutlet UIView *headerView;

@end

@implementation BNRItemsViewController


-(instancetype)init
{
    //Initialize the view controller, then update the initializer to add 5 random items to the BNRItemStore. Then, sort the items by item value. If the item's value is >50, then put it in the expensive list. Else, put it in the inexpensive list
    self = [super initWithStyle:UITableViewStylePlain];
    
    
    if (self)
    {
        /*
        for (int i = 0; i < 5; i++)
        {
            [[BNRItemStore sharedStore] createItem];
        }*/
    }
    return self;
}

-(instancetype)initWithStyle:(UITableViewStyle)style
{
    return [self init];
}


-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    NSArray *allStoreItems = [[BNRItemStore sharedStore]allItems];
    
    if(allStoreItems.count == 0)
    {
        return 1;
    }
    
    else
    {
        return 3;
    }
}

-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    NSArray *allStoreItems = [[BNRItemStore sharedStore]allItems];
    
    if (allStoreItems.count == 0)
    {
        return @"";
    }
    
    else
    {
        if (section == 0)
        {
            return @"< $50";
        }
        
        else if (section == 1)
        {
            return @"> $50";
        }
        
        else
        {
            return @"";
        }

    }
}


-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    NSArray *allStoreItems = [[BNRItemStore sharedStore]allItems];
    
    if(allStoreItems.count == 0)
    {
        return 1;
    }
    
    else
    {
        if (section == 0)
        {
            return [[[BNRItemStore sharedStore]inexpensiveItems]count];
        }
        
        else if (section == 1)
        {
            return [[[BNRItemStore sharedStore]expensiveItems]count];
        }
        
        else
        {
            return 1;
        }
    }
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    //Find the LAST row in the LAST section, then set its (and others') height. MUST use self tableView:tableView for this to work!
    NSInteger lastSection = [tableView numberOfSections] - 1;
    NSInteger lastRow = [self tableView:tableView numberOfRowsInSection:lastSection] - 1;
    NSIndexPath *lastSectionRow = [NSIndexPath indexPathForRow:lastRow inSection:lastSection];
    
    if (indexPath == lastSectionRow)
    {
        return  44;
    }
    
    else
    {
        return 60;
    }
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    /*
    //Create an instance of UITableViewCell, with default appearance
    UITableViewCell *tableViewCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"UITableViewCell"];*/
    
    //Make this method reuse cells to preserve iOS memory
    UITableViewCell *tableViewCell = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell" forIndexPath:indexPath];
    
    /*
    //Set the text on the cell with the description of the item that is at the nth index of items, where n = row this cell will appear in on the table view
    NSArray *items = [[BNRItemStore sharedStore]allItems];
    BNRItem *item = items[indexPath.row];
    
    tableViewCell.textLabel.text = [item description];*/
    
    NSInteger lastSection = [tableView numberOfSections] - 1;
    NSInteger lastRow = [self tableView:tableView numberOfRowsInSection:lastSection] - 1;
    NSIndexPath *lastSectionRow = [NSIndexPath indexPathForRow:lastRow inSection:lastSection];
    
    if (indexPath == lastSectionRow)
    {
        tableViewCell.textLabel.text = @"No more items!";
    }
    
    else
    {
        if(indexPath.section == 0)
        {
            NSArray *inExpensiveItems = [[BNRItemStore sharedStore]inexpensiveItems];
            BNRItem *item = inExpensiveItems[indexPath.row];
            tableViewCell.textLabel.font = [UIFont systemFontOfSize:20];
            tableViewCell.textLabel.text = [item description];
        }
        
        else
        {
            NSArray *exPensiveItems = [[BNRItemStore sharedStore]expensiveItems];
            BNRItem *item = exPensiveItems[indexPath.row];;
            tableViewCell.textLabel.font = [UIFont systemFontOfSize:20];
            tableViewCell.textLabel.text = [item description];
        }
    }
    
    return tableViewCell;
}

-(IBAction)addNewItem:(id)sender
{
    //Create a new item BEFORE you ask the tableView to insert a new row!
    BNRItem *newItem = [[BNRItemStore sharedStore]createItem];
    
    //Figure out where that item is in the array
    //NSInteger lastRow = [[[BNRItemStore sharedStore]allItems]indexOfObject:newItem];
    NSInteger lastRow;
    
    
    //If there are no items in the store at the beginning, then there is only one section saying "No more items!" Then we will need to insert 2 sections.
    if (self.tableView.numberOfSections == 1)
    {
        [self.tableView beginUpdates];
        [self.tableView insertSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationAutomatic];
        [self.tableView insertSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationAutomatic];
        [self.tableView endUpdates];
    }
    
    else
    {
        [self.tableView beginUpdates];
        
        if (newItem.valueInDollars < 50)
        {
            lastRow =[[[BNRItemStore sharedStore]inexpensiveItems]indexOfObject:newItem];
            //If this new item is inexpensive, then put it in the last row in 1st section
            NSIndexPath *indexPath = [NSIndexPath indexPathForRow:lastRow inSection:0];
            //Now insert this row into the table
            [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationTop];
        }
        
        else
        {
            lastRow = [[[BNRItemStore sharedStore]expensiveItems]indexOfObject:newItem];
            //If this new item is expensive, then put it in the last row in the second section
            NSIndexPath *indexPath = [NSIndexPath indexPathForRow:lastRow inSection:1];
            [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationTop];
        }
        
        [self.tableView endUpdates];
    }
}

-(IBAction)toggleEditingMode:(id)sender
{
    //If you are currently in editing mode...
    if (self.isEditing)
    {
        //Change the text of the button to inform the user of the state
        [sender setTitle:@"Edit" forState:UIControlStateNormal];
        
        //Turn off editing mode
        [self setEditing:NO animated:YES];
    }
    
    else
    {
        //Change the text of the button to inform user of the state
        [sender setTitle:@"Done" forState:UIControlStateNormal];
        
        //Turn on editing mode
        [self setEditing:YES animated:YES];
    }
}

-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    //If the tableView is asking to commit a delete command
    if (editingStyle == UITableViewCellEditingStyleDelete)
    {
        if (indexPath.section == 0)
        {
            NSArray *inExpensiveItems = [[BNRItemStore sharedStore]inexpensiveItems];
            BNRItem *item = inExpensiveItems[indexPath.row];
            [[BNRItemStore sharedStore]removeItem:item];
            //Also remove that row from the tableView with an animation
            [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
        }
        
        else if (indexPath.section == 1)
        {
            NSArray *exPensiveItems = [[BNRItemStore sharedStore]expensiveItems];
            BNRItem *item = exPensiveItems[indexPath.row];
            [[BNRItemStore sharedStore]removeItem:item];
            //Also remove that row from the tableView with an animation
            [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
        }
    }
}

-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSInteger lastSection = [tableView numberOfSections] - 1;
    NSInteger lastRow = [self tableView:tableView numberOfRowsInSection:lastSection] - 1;
    NSIndexPath *lastSectionRow = [NSIndexPath indexPathForRow:lastRow inSection:lastSection];
    
    if (indexPath == lastSectionRow)
    {
        return  NO;
    }
    
    else
    {
        return  YES;
    }
}

-(void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{
        [[BNRItemStore sharedStore]moveItemAtIndex:sourceIndexPath.row toIndex:destinationIndexPath.row];
}

-(NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath
{
    NSUInteger previousSection = sourceIndexPath.section;
    NSUInteger destinationSection = proposedDestinationIndexPath.section;
    NSUInteger endSection = [tableView numberOfSections] - 1;
    NSUInteger endRow = [self.tableView numberOfRowsInSection:endSection] - 1;
    
    if (previousSection != destinationSection)
    {
        return [NSIndexPath indexPathForRow:sourceIndexPath.row inSection:previousSection];
    }
    
    else if (proposedDestinationIndexPath.row == endRow)
    {
        return [NSIndexPath indexPathForRow:sourceIndexPath.row inSection:previousSection];
    }
    
    else
    {
        return proposedDestinationIndexPath;
    }
}


-(NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return @"Remove";
}

-(UIView *)headerView
{
    //If you have not loaded the header view yet...
    if(!_headerView)
    {
        //Load headerview.xib
        [[NSBundle mainBundle]loadNibNamed:@"HeaderView" owner:self options:nil];
    }
    
    return  _headerView;
}

-(void)viewDidLoad
{
    [super viewDidLoad];
    
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"UITableViewCell"];
    
    UIView *header = self.headerView;
    [self.tableView setTableHeaderView:header];
}

@end

BNRItemStore.h

[code] #import <Foundation/Foundation.h>

@class BNRItem;

@interface BNRItemStore : NSObject

@property (nonatomic, readonly, copy) NSArray *allItems;
@property (nonatomic, copy) NSMutableArray *expensiveItems;
@property (nonatomic, copy) NSMutableArray *inexpensiveItems;

+(instancetype)sharedStore;
-(BNRItem *)createItem;
-(void)removeItem:(BNRItem *)item;
-(void)moveItemAtIndex:(NSUInteger)fromIndex
toIndex:(NSUInteger)toIndex;

@end
[/code]

BNRItemStore.m

[code] #import “BNRItemStore.h”
#import “BNRItem.h”

@interface BNRItemStore ()

@property (nonatomic) NSMutableArray *privateItems;
@property (nonatomic) NSMutableArray *privateItemsOverFifty;
@property (nonatomic) NSMutableArray *privateItemsUnderFifty;

@end

@implementation BNRItemStore

//BNRItemsViewController will initialize an instance of BNRItems Store. If there is no instance, then it will call the private initializer, which will initialize its one mutable array, privateItems. If there is an instance, then we will return that one instance, so as not to make more than one instance of this singleton.
+(instancetype)sharedStore
{
static BNRItemStore *sharedStore;

if(!sharedStore)
{
    sharedStore = [[self alloc]initPrivate];
}

return sharedStore;

}

//If a programmer calls [BNRItemStore alloc]init] let her know the error of her ways
-(instancetype)init
{
[NSException raise:@“Singleton” format:@“Use + [BNRItemStore sharedStore]”];
return nil;
}

//Here is the real (secret, as we only need one initialized instance) initializer. This will initialize the BNRItemStore, and initialize its mutable array, privateItems
-(instancetype)initPrivate
{
self = [super init];
if (self)
{
self.privateItems = [[NSMutableArray alloc]init];

    //We will allocate 2 arrays for items over $50, and under. We will use these sorted arrays to populate the two sections of the table view
    self.privateItemsOverFifty = [[NSMutableArray alloc]init];
    self.privateItemsUnderFifty = [[NSMutableArray alloc]init];
    
}

return self;

}

//Other classes will call for allItems instead of the singleton’s private array. Allitems is read only, and thus not changeable
-(NSArray *)allItems
{
return [self.privateItems copy];
}

//Create a BNRItem and add it to the private array
-(BNRItem *)createItem
{
//Create an item. If the item’s value is under $50, put it in the inexpensive array. Else, put it in the expensive array. Then return the item.
BNRItem *item = [BNRItem randomItem];

//[self.privateItems addObject:item];

if (item.valueInDollars < 50)
{
    [self.privateItemsUnderFifty addObject:item];
}

else
{
    [self.privateItemsOverFifty addObject:item];
}

[self.privateItems addObjectsFromArray:self.privateItemsUnderFifty];
[self.privateItems addObjectsFromArray:self.privateItemsOverFifty];

return item;

}

-(void)removeItem:(BNRItem *)item
{
[self.privateItems removeObjectIdenticalTo:item];

if (item.valueInDollars < 50)
{
    [self.privateItemsUnderFifty removeObjectIdenticalTo:item];
}

else
{
    [self.privateItemsOverFifty removeObjectIdenticalTo:item];
}

}

-(void)moveItemAtIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex
{
if (fromIndex == toIndex)
{
return;
}

//Get a pointer to the object being moved so that you can reinsert it
BNRItem *item = self.privateItems[fromIndex];

//Remove the object from the array
[self.privateItems removeObjectAtIndex:fromIndex];

//Now reinsert it at the new location
[self.privateItems insertObject:item atIndex:toIndex];

}

-(NSMutableArray *)inexpensiveItems
{
//Take our private under $50 array and copy it so that the rest of the program can use the public, read-only version of the under $50 array (inexpensiveItems
return [self.privateItemsUnderFifty copy];
}

-(NSMutableArray *)expensiveItems
{
return [self.privateItemsOverFifty copy];
}
@end
[/code]