Question

In my app, I am trying to save the pins that are on the map so that they are there when the user opens the app after it is terminated. I have conformed my mkAnnotation class to NSCoding, and implemented the two required methods. The annotations are all stored in a NSMutableArray in a singleton class, so I am really just trying to save the array in the singleton class. Everything is being encoded fine, but I do not think they are being decoded. Here is some code: This is my MKAnnotation class:

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

@interface MapPoint : NSObject <MKAnnotation, NSCoding>
{
}

- (id)initWithAddress:(NSString*)address
        coordinate:(CLLocationCoordinate2D)coordinate
                title:(NSString *)t;

@property (nonatomic, readwrite) CLLocationCoordinate2D coordinate;

//This is an optional property from MKAnnotataion
@property (nonatomic, copy) NSString *title;
@property (nonatomic, readonly, copy) NSString *subtitle;
@property (nonatomic) BOOL animatesDrop;
@property (nonatomic) BOOL canShowCallout;

@property (copy) NSString *address;
@property (nonatomic, copy) NSString *imageKey;
@property (nonatomic, copy) UIImage *image;

@end 

@implementation MapPoint

@synthesize title, subtitle, animatesDrop, canShowCallout, imageKey, image;
@synthesize address = _address, coordinate = _coordinate;

-(id)initWithAddress:(NSString *)address
       coordinate:(CLLocationCoordinate2D)coordinate
            title:(NSString *)t {
    self = [super init];

    if (self) {
        _address = [address copy];
        _coordinate = coordinate;

        [self setTitle:t];

        NSDate *theDate = [NSDate date];

        subtitle = [NSDateFormatter localizedStringFromDate:theDate
                                                  dateStyle:NSDateFormatterShortStyle
                                                  timeStyle:NSDateFormatterShortStyle];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {

    [aCoder encodeObject:_address forKey:@"address"];

    NSLog(@"ENCODING coordLatitude %f coordLongitude %f ", _coordinate.latitude, _coordinate.longitude);
    [aCoder encodeDouble:_coordinate.longitude forKey:@"coordinate.longitude"];
    [aCoder encodeDouble:_coordinate.latitude forKey:@"coordinate.latitude"];

    [aCoder encodeObject:title forKey:@"title"];
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super init];
    if (self) {
        [self setAddress:[aDecoder decodeObjectForKey:@"address"]];

        NSLog(@"DECODING coordLatitude %f coordLongitude %f ", _coordinate.latitude, _coordinate.longitude);
        _coordinate.longitude = [aDecoder decodeDoubleForKey:@"coordinate.longitude"];
        _coordinate.latitude = [aDecoder decodeDoubleForKey:@"coordinate.latitude"];

        [self setTitle:[aDecoder decodeObjectForKey:@"title"]];
    }
    return self;
}

@end

Here is my singleton class:

#import <Foundation/Foundation.h>
@class MapPoint;

@interface Data : NSObject
{
NSMutableArray *_annotations;
}

@property (retain, nonatomic) NSMutableArray *annotations;

+ (Data *)singleton;
- (NSString *)pinArchivePath;
- (BOOL)saveChanges;

@end

@implementation Data

@synthesize annotations = _annotations;

+ (Data *)singleton {
    static dispatch_once_t pred;
    static Data *shared = nil;
    dispatch_once(&pred, ^{
        shared = [[Data alloc] init];
        shared.annotations = [[NSMutableArray alloc]init];
    });
    return shared;
}

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

- (NSString *)pinArchivePath {
    NSArray *cachesDirectories = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);

    NSString *cachesDirectory = [cachesDirectories objectAtIndex:0];

    return [cachesDirectory stringByAppendingPathComponent:@"pins.archive"];
}

- (BOOL)saveChanges {
    NSString *path = [self pinArchivePath];

    return [NSKeyedArchiver archiveRootObject:[Data singleton].annotations
                                       toFile:path];
}

@end

In my viewDidLoad method on the map view controller, I try and place the annotations in the singleton array on the map with this:

for (MapPoint *mp in [Data singleton].annotations) {
        [_worldView addAnnotation:mp];
}
Was it helpful?

Solution

The main problem is in the singleton method in these lines:

dispatch_once(&pred, ^{
    shared = [[Data alloc] init];
    shared.annotations = [[NSMutableArray alloc]init]; //<-- problem line
});

The shared = [[Data alloc] init]; line decodes and initializes the annotations array.

Then the shared.annotations = [[NSMutableArray alloc]init]; line re-creates and re-initializes the annotations array thus discarding the just-decoded annotations so the singleton always returns an empty array.

Remove the shared.annotations = [[NSMutableArray alloc]init]; line.


As already mentioned in the comment, the other minor issue, which causes simply confusion, is the placement of the NSLog where the coordinate is being decoded. The NSLog should be after the decode is done.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top