Update the Model and add a Store

Before going too far, it's important to pull the previous GasCan definition into this project. It's also necessary to add a 'name' property to the Gas Can. (After all, I'll need a name to display.)

The easiest way to import the files is to select File -> Add files to "GasCanManager"... From the dialog box that's displayed, navigate to the old location, select the files, and specify that a copy should be made into the local project.

Once that's done, it's necessary to add the name property. As review, the two files should look as follows:

GasCan.h

  1. #import <Foundation/Foundation.h>
  2.  
  3. @interface GasCan : NSObject {
  4. @private
  5. int size;
  6. @protected
  7. BOOL spoutOpen;
  8. BOOL valveOpen;
  9. }
  10.  
  11. @property (nonatomic) float level;
  12. @property (nonatomic) NSString *name;
  13.  
  14. -(id) initWithSize: (int)x andName: (NSString*) canName;
  15. -(id) init;
  16. -(void) pourContents;
  17. -(int) size;
  18. -(void)openSpout: (BOOL)x andValve: (BOOL)y;
  19. @end

GasCan.m

  1. #import "GasCan.h"
  2.  
  3. @implementation GasCan
  4.  
  5. @synthesize level;
  6. @synthesize name;
  7.  
  8. // Call the designated initializer and default the can to a size of 5
  9. -(id) init {
  10. return [self initWithSize:5 andName: @"New Can"];
  11. }
  12.  
  13. // The designated initializer
  14. -(id) initWithSize: (int)x andName: (NSString*) canName {
  15. self = [super init];
  16. if (self) {
  17. size = x;
  18. name = canName;
  19. }
  20. return self;
  21. }
  22.  
  23. -(void) pourContents {
  24. if (!spoutOpen) {
  25. NSLog(@"You can't pour the contents because the spout is not open.");
  26. } else {
  27. float increment = (valveOpen) ? .1 : .01;
  28. while (level > 0.0) {
  29. NSLog(@"%@ Glug glug glug", name);
  30. level -= increment;
  31. }
  32. level = 0;
  33. }
  34. }
  35.  
  36. -(void)setLevel: (float)l {
  37. level = (l > size) ? size : l;
  38. }
  39.  
  40. -(int) size {
  41. return size;
  42. }
  43.  
  44. -(void)openSpout: (BOOL)x andValve: (BOOL)y {
  45. spoutOpen = x;
  46. valveOpen = y;
  47. }
  48.  
  49. @end

When writing an iOS application, it's often necessary to manage a collection of data. This is done in a "store". Since this application will be managing gas cans, it's necessary to create a gas can store. Create a new Objective-C class (based on NSObject), and name it GasCanStore.

Open GasCanStore.h and update it to look as follows:

  1. #import <Foundation/Foundation.h>
  2.  
  3. @class GasCan;
  4.  
  5. @interface GasCanStore : NSObject{
  6. NSMutableArray *allCans;
  7. }
  8.  
  9. // Singleton
  10. +(GasCanStore *)canStore;
  11.  
  12. -(NSArray *)allCans;
  13. -(GasCan *)createCan: (int)newSize withName: (NSString *)newName;
  14.  
  15. @end

Some items of note:

  • To improve compilation time, GasCan is identified as a class
  • This store has a single property: A mutable array named allCans
  • This class will be a singleton class: Only one instance can exist within the application
  • Two helper methods exist to get a reference to all gas cans, and to create a new gas can

Now, switching to the implementation file, the GasCanStore.m file should be updated as follows:

  1. #import "GasCanStore.h"
  2. #import "GasCan.h"
  3.  
  4. @implementation GasCanStore
  5.  
  6. +(GasCanStore *)canStore {
  7. static GasCanStore *canStore = nil;
  8. if (!canStore) {
  9. canStore = [[super allocWithZone:nil] init];
  10. }
  11. return canStore;
  12. }
  13.  
  14. +(id)allocWithZone:(NSZone *)zone {
  15. return [self canStore];
  16. }
  17.  
  18. -(id) init {
  19. self = [super init];
  20. if (self) {
  21. allCans = [[NSMutableArray alloc] init];
  22. }
  23. return self;
  24. }
  25.  
  26. -(NSArray *) allCans {
  27. return allCans;
  28. }
  29.  
  30. -(GasCan *)createCan: (int)newSize withName: (NSString *)newName {
  31. GasCan *gc = [[GasCan alloc] initWithSize:newSize andName:newName];
  32. [allCans addObject:gc];
  33. return gc;
  34. }
  35.  
  36. @end

The most important thing to note is the class-level method named 'canStore'. This method contains a static variable, and once instantiated, always returns the exact same instance. Also, to ensure that multiple instances are never created, allocWithZone is overridden to return the existing instance. Also, since this object's superclass can only be called by itself, the allocWithZone method ensures that multiple instances don't come into existance.

Lastly, a convenience method exists to create a gas can, and automatically add it to the store.

Now that the model and store have been created, it's time to display a collection of items in the user interface.