Jesse Nelson
Jesse Nelson

Reputation: 796

Best way to save a hierarchy of custom Objective-c objects for persistence

I am writing an app that allows the user to time multiple routes they may choose to various locations and then have an archive of saved data they can view. The problem I am having is saving the data for persistent use. I have read up on NSCoding protocol but it doesn't seem to fit my objects needs due to incompatible properties that exits in my objects such as CLCoordinateLocation2D( I know this one I can break into more components such as latitude and longitude, but I am thinking there has to be a better way) but there are others. I am coming from java where all I need to do is implement the serializable interface and I am good to go but I am new to Objective-C and unsure of the best way to approach the problem.

Here are the object structures of what I need serialized.

@interface BestRouteBrain : NSObject < CLLocationManagerDelegate, NSCoding > {
    NSDictionary *segments;
    BestRouteTimer *timer;
    BestRouteSegment *currentSegment;
    //Manages attributes that communicate with GPS
    CLLocationManager *locationManager;
    Firebase *firebase;
   //NSDictionary *routesByTimeOfDay;
   //NSDictionary *routesByAverage;
}

@property ( strong ) CLLocationManager *locationManager;
@property BestRouteSegment *currentSegment;
@property BestRouteTimer *timer;
@property NSDictionary *segments;
@property Firebase *firebase;

Basically, all I need to serialize is the NSDictionary *segments. Here is what a segment looks like

@interface BestRouteSegment :NSObject <NSCoding>{
    // Name of segment
    NSString *segmentName;
    // String reprsenting starting location for segment
    NSString *startingLocation;
    // String representing ending location for segment
    NSString *endingLocation;
    //Key is name of route and value is a route object;
    NSDictionary *routesForSegment;
    CLLocationCoordinate2D startCoord;
    CLLocationCoordinate2D endCoord;
}

@property CLLocationCoordinate2D startCoord;
@property CLLocationCoordinate2D endCoord;
@property NSString *startingLocation;
@property NSString *endingLocation;
@property NSString *segmentName;
@property NSDictionary *routesForSegment;

All properties in this object need to be serialized as well, including the NSDictionary *routesForSegment. Here is what a route looks like,

@interface Route : NSObject {
    // Name of route given by user
    NSString *routeName;
    // The total average time for all trips using this route
    double  avgRouteTime;
    // Array of trips which used this route
    NSArray *allTripsFromRoute;
}

@property NSString *routeName;
@property double avgRouteTime;
@property NSArray *allTripsFromRoute;

All of these properties need to be serialized as well. Finally, here is what a trip looks like.

@interface BestRouteTrip : NSObject {
    // Time elapsed for current trip
    double tripTime;
    // String representing time of day - Departure time is
    NSString *timeOfDay;
    NSDate *morningBeforeTrafic;// from 12:00am to 6:30am
    NSDate *morningTrafic; // from 6:30am to 9:30am
    NSDate *morningAfterTrafic; // from 9:30am to 12:00pm
    NSDate *afternoon;    // from 12:00pm to 3:30pm
    NSDate *eveningTrafic; // from 3:30pm to 6:30pm
    NSDate *eveningAfterTrafic; // from 6:30pm to 12:00p
}

@property double tripTime;
@property NSString *timeOfDay;
@property NSDate *morningBeforeTrafic;// from 12:00am to 6:30am
@property NSDate *morningTrafic; // from 6:30am to 9:30am
@property NSDate *morningAfterTrafic; // from 9:30am to 12:00pm
@property NSDate *afternoon;    // from 12:00pm to 3:30pm
@property NSDate *eveningTrafic; // from 3:30pm to 6:30pm
@property NSDate *eveningAfterTrafic; // from 6:30pm to 12:00p

The only two values I need to serialize here are, double tripTime and NSString *timeOfDay.

Summary: The brain has a dictionary of segments which needs to be serialized. The segment has it's properties to be serialized including a dictionary of routes(another custom object). Routes has its' properties to be serialized including an array of Trips. Finally, trips has its' two values that need to be serialized.

Thanks in advance for any assistance.

Upvotes: 1

Views: 299

Answers (1)

Jesse Nelson
Jesse Nelson

Reputation: 796

Ok, so I solved this problem by implementing the NSCoding protocol inside of BestRouteSegment which encodes my segments without error. Here are the methods that abide by the mentioned protocol.

- (void)encodeWithCoder:(NSCoder *)encoder {
    NSLog(@"Encode in segment was called");
    [ encoder encodeDouble:self.startCoord.latitude
                    forKey:@"startCoord.latitude" ];
    [ encoder encodeDouble:self.startCoord.longitude
                    forKey:@"startCoord.longitude" ];
    [ encoder encodeDouble:self.endCoord.latitude
                    forKey:@"endCoord.latitude" ];
    [ encoder encodeDouble:self.endCoord.longitude 
                    forKey:@"endCoord.longitude" ];
    [ encoder encodeObject:self.segmentName
                    forKey:@"segmentName" ];
    [ encoder encodeObject:self.startingLocation
                    forKey:@"startingLocation" ];
    [ encoder encodeObject:self.endingLocation
                    forKey:@"endingLocation" ];
    [ encoder encodeObject:self.routesForSegment
                    forKey:@"routesForSegment" ];
}

- (id)initWithCoder:(NSCoder *)adecoder {
    NSLog(@"Init with coder was called in segment");
    if ( self = [ super init ] ) {
        self.startCoord =
        CLLocationCoordinate2DMake(
                                   [ adecoder decodeDoubleForKey:@"startCoord.lattitude" ],
                                   [ adecoder decodeDoubleForKey:@"startCoord.longitude" ]
                                   );
        self.endCoord =
        CLLocationCoordinate2DMake(
                                   [ adecoder decodeDoubleForKey:@"endCoord.lattitude" ],
                                   [ adecoder decodeDoubleForKey:@"endCoord.longitude" ]
                                   );
        self.segmentName =
        [ adecoder decodeObjectForKey:@"segmentName" ];
        self.startingLocation =
        [ adecoder decodeObjectForKey:@"startingLocation" ];
        self.endingLocation =
        [ adecoder decodeObjectForKey:@"endingLocation" ];
        self.routesForSegment =
        [ adecoder decodeObjectForKey:@"routesForSegment" ];

    }
    return self;
}

Upvotes: 1

Related Questions