2
votes

I have the following implementation of the MKAnnotation protocol on a NSManagedObject subclass called Restaurant:

Interface:

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


@interface Restaurant : NSManagedObject <MKAnnotation> {
    CLLocationCoordinate2D coordinate;
}

@property (nonatomic, assign) CLLocationCoordinate2D primitiveCoordinate;
@property (nonatomic, copy) NSString * title;
@property (nonatomic, copy) NSString * subtitle;
@property (nonatomic, retain) NSNumber * longtitude;
@property (nonatomic, retain) NSNumber * latitude;

@end

Implementation:

#import "Restaurant.h"


@implementation Restaurant

@dynamic title;
@dynamic subtitle;
@dynamic coordinate;
@dynamic longtitude;
@dynamic latitude;

-(void)setCoordinate:(CLLocationCoordinate2D)newCoordinate{
    [self willChangeValueForKey:@"coordinate"];
    [self setPrimitiveCoordinate:newCoordinate];
    [self didChangeValueForKey:@"coordinate"];
    [self setValue:[NSNumber numberWithDouble:newCoordinate.latitude] forKey:@"latitude"];
    [self setValue:[NSNumber numberWithDouble:newCoordinate.longitude] forKey:@"longtitude"];
}

-(CLLocationCoordinate2D)coordinate{
    [self willAccessValueForKey:@"coordinate"];
    CLLocationCoordinate2D temp = [self primitiveCoordinate];
    [self didAccessValueForKey:@"coordinate"];
    return temp;
}

-(void)setPrimitiveCoordinate:(CLLocationCoordinate2D)primitiveCoordinate{
    coordinate = primitiveCoordinate;
}

-(CLLocationCoordinate2D)primitiveCoordinate{

    return coordinate;
}

-(void)awakeFromFetch{
    [super awakeFromFetch];

    double longtitude = [[self longtitude] doubleValue];
    double latitude = [[self latitude] doubleValue];

    if(!isnan(longtitude) && !isnan(latitude)){
        CLLocationCoordinate2D temp = CLLocationCoordinate2DMake(latitude, longtitude);
        [self setPrimitiveCoordinate:temp];
    }

}

@end

I followed Apples Core Data programming guide about non-standard persistent attributes and the documentation of the MKAnnotation protocol to implement MKAnnotation's CLocation2DCoordinate property as a transient instance variable as Core Data does not support storing C structs.

My questions are: Is this implementation correct or would you change something in my implementation? Maybe use NSValue to store the C struct instead of the NSNumber objects (latitude and longtitude)? Which advantages and disadvantages would appear when using NSValue instead? What about the rest of the code?

Thanks!

1

1 Answers

0
votes

The issue with implementing coordinate as a transient as you have done is when you set the latitude or longitude the coordinate KVO won't fire. So you might be better off implementing it as a computed property instead. You also are missing the awakeFromSnapshotEvents which is required for updating the coordinate when a child context saves with a Restaurant that has updated latitude or longitude when you don't have keyPathsForValuesAffecting which would have done that automatically for you.

For a computed property it is:

// this means setting latitude or longitude causes coordinate KVO notification to be sent which the map is observing.
+ (NSSet<NSString *> *)keyPathsForValuesAffectingCoordinate
{
    return [NSSet setWithObjects:@"latitude", @"longitude", nil];
}

- (CLLocationCoordinate2D)coordinate{
    return CLLocationCoordinate2DMake(self.latitude, self.longitude);
}

- (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate{
    self.latitude = newCoordinate.latitude;
    self.longitude = newCoordinate.longitude;
}

At first glance it looks like there is a issue with setCoordinate sending too many notifications but the map actually updates its annotation views on the next runloop so that shouldn't matter.