My Silver Challenge Solution


#1

Here is my solution for the Silver Challenge for this chapter. I only included the files with changes. I used the sharedStore concept and am getting used to this. I switched the creation of the map point to the store as well as I’m supposing this is part of the store paradigm. If there any suggestions, I’d love to hear them. Also, if you see any chinks in my armor let me know. Thanks.

WhereamiAppDelegate.m

#import "WhereamiAppDelegate.h"
#import "WhereamiViewController.h"
#import "BNRMapPointStore.h"

@implementation WhereamiAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.viewController = [[WhereamiViewController alloc] initWithNibName:@"WhereamiViewController" bundle:nil];
    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];
    return YES;
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    
    BOOL success = [[BNRMapPointStore sharedStore] savePointsToFile];
    
    if (success)
    {
        NSLog(@"MapPoints saved.");
    }
    else
    {
        NSLog(@"Problem with save!");
    }
}

WhereamiViewController.m

#import "WhereamiViewController.h"
#import "BNRMapPoint.h"
#import "BNRMapPointStore.h"

@interface WhereamiViewController ()

@end



@implementation WhereamiViewController

-(void)findLocation
{
    [locationManager startUpdatingLocation];
    [activityIndicator startAnimating];
    [locationTitleField setHidden:YES];
}

-(void)foundLocation:(CLLocation *)loc
{
    CLLocationCoordinate2D coord = [loc coordinate];
    
    //Create BNRMapPoint from sharedStore
    BNRMapPoint *mp = [[BNRMapPointStore sharedStore] createMapPointWithCoordinate:coord
                                                                                                                    andTitle:[locationTitleField text]];
    
    [worldView addAnnotation:mp];//This works because bnrmappoint conforms to mkannotation
    
    MKCoordinateRegion region =  MKCoordinateRegionMakeWithDistance(coord, 250, 250);
    
    [worldView setRegion:region animated:YES];
    
    //Reset the UI
    [locationTitleField setText:@""];
    [activityIndicator stopAnimating];
    [locationTitleField setHidden:NO];
    [locationManager stopUpdatingLocation];
    
    
}

-(void)viewDidLoad
{
    [worldView setShowsUserLocation:YES];
    
//  Load the archived MapPoints
    NSArray *mpArray = [[BNRMapPointStore sharedStore] pointsArray];
    [worldView addAnnotations:mpArray];
}

BNRMapPoint.h


#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>

@interface BNRMapPoint : NSObject <MKAnnotation, NSCoding>
{
    double longitude;
    double latitude;
    
}

-(id)initWithCoordinate:(CLLocationCoordinate2D)c
                            title:(NSString *)t;

@property (nonatomic, readonly)CLLocationCoordinate2D coordinate;
@property (nonatomic, copy)NSString *title;
@property (nonatomic, strong)NSDateFormatter *dateFormatter;
@property (nonatomic, copy)NSString *subtitle;

@end

BNRMapPoint.m

#import "BNRMapPoint.h"

@implementation BNRMapPoint

@synthesize coordinate, title, dateFormatter, subtitle;

-(id)initWithCoordinate:(CLLocationCoordinate2D)c
                            title:(NSString *)t
{
    self = [super init];
    if(self)
    {
        coordinate = c;
        
        //Add date to title
        dateFormatter = [[NSDateFormatter alloc] init];
        [dateFormatter setDateStyle:NSDateFormatterMediumStyle];
        [dateFormatter setTimeStyle:NSDateFormatterNoStyle];
        NSDate *today = [NSDate date];
        NSString *dateString = [dateFormatter stringFromDate:today];
        [self setTitle:t];
        [self setSubtitle:dateString];
        
    }
    
    return self;
}

-(id)init
{
    return [self initWithCoordinate:CLLocationCoordinate2DMake(43.0, -89.32) title:@"Hometown"];
}

-(id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super init];
    if (self)
    {
        latitude = [aDecoder decodeDoubleForKey:@"latitude"];
        longitude = [aDecoder decodeDoubleForKey:@"longitude"];
        coordinate = CLLocationCoordinate2DMake(latitude, longitude);
        [self setTitle:[aDecoder decodeObjectForKey:@"title"]];
        [self setSubtitle:[aDecoder decodeObjectForKey:@"subtitle"]];
    }
    return self;
}

-(void)encodeWithCoder:(NSCoder *)aCoder
{
    latitude  = coordinate.latitude;
    longitude = coordinate.longitude;
    
    [aCoder encodeDouble:latitude  forKey:@"latitude"];
    [aCoder encodeDouble:longitude forKey:@"longitude"];
    [aCoder encodeObject:title     forKey:@"title"];
    [aCoder encodeObject:subtitle  forKey:@"subtitle"];
    
}

@end

BNRMapPointStore.h


#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>

@class BNRMapPoint;

@interface BNRMapPointStore : NSObject
{
    NSMutableArray *pointsArray;
}

+(BNRMapPointStore *)sharedStore;
-(NSArray *)pointsArray;
-(NSString *)archivePath;
-(BOOL)savePointsToFile;
-(BNRMapPoint *)createMapPointWithCoordinate:(CLLocationCoordinate2D)c
                                                          andTitle:(NSString *)t;

@end

BNRMapPointStore.m

#import "BNRMapPointStore.h"
#import "BNRMapPoint.h"

@implementation BNRMapPointStore

+(BNRMapPointStore *)sharedStore
{
    static BNRMapPointStore *sharedStore = nil;
    
    if (!sharedStore) {
        sharedStore = [[super allocWithZone:nil] init];
    }
    
    return sharedStore;
}

+(id)allocWithZone:(NSZone *)zone
{
    return [self sharedStore];
}

-(id)init
{
    self = [super init];
    if (self)
    {
        NSString *path = [self archivePath];
        pointsArray    = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
    }
    
    if (!pointsArray)
    {
        pointsArray = [[NSMutableArray alloc] init];
    }
    
    return self;
}

-(BNRMapPoint *)createMapPointWithCoordinate:(CLLocationCoordinate2D)c
                                                          andTitle:(NSString *)t
{
    BNRMapPoint * mp = [[BNRMapPoint alloc] initWithCoordinate:c title:t];
    [pointsArray addObject:mp];
    return mp;
    
    
}

-(NSArray *)pointsArray
{
    return pointsArray;
}

-(NSString *)archivePath
{
    NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentDorectory = [documentDirectories objectAtIndex:0];
    return [documentDorectory stringByAppendingPathComponent:@"points.archive"];
}

-(BOOL)savePointsToFile
{
    NSString *path = [self archivePath];
    
    NSLog(@"MapPoint saved to: %@", path);
    
    return [NSKeyedArchiver archiveRootObject:pointsArray
                                                           toFile:path];
}

@end

#2

Apparently, I skipped a section of Chapter 14. I missed the section about writeToFile:. Thus my code uses the archiving methodology which does not utilize atomic procedures. How big a deal is this?


#3

Unlike you, I decided to save the CLLocation class object instead of saving the two doubles of CLLocationCoordinate2D.
So here’s my BNRMapPoint.m

[code]- (void)encodeWithCoder:(NSCoder *)aCoder
{
CLLocation *c = [[CLLocation alloc] initWithLatitude:coordinate.latitude longitude:coordinate.longitude];
[aCoder encodeObject:c forKey:@“coordinate”];
[aCoder encodeObject:title forKey:@“title”];
}

  • (id)initWithCoder:(NSCoder *)aDecoder
    {
    self = [super init];
    if (self) {
    coordinate = [[aDecoder decodeObjectForKey:@“coordinate”] coordinate];
    title = [aDecoder decodeObjectForKey:@“title”];
    }

    return self;
    }

[/code]

Also, I didn’t take the book’s solution of creating the item within the store. I didn’t know what good that did when you had to add the item to the store’s array instance variable anyway.
So, my method in MapPointStore.m looks like this:

- (void)addPoint:(BNRMapPoint *)point { [allPoints addObject:point]; }

And all I did to the ViewController is add [[MapPointStore sharedStore] addPoint:mp]; after the BNRMapPoint object was made.