UPDATE: SOLUTION DOCUMENTED IN ANSWERS BELOW
I'm having trouble with RestKit mapping using the new @root @parent accessors in 0.20.3. I'm not sure if it's a bug or a misunderstanding of how to use the framework properly.
PROBLEM
The new concept of @root and @parent doesn't seem to be working for me.
EDIT: Removed a bunch of discussion about what I thought the problem was. I was way wrong so there is no need in digesting it. If the problem statement above applies to you... then this SO post might help get you going.
BACKGROUND
Example source XML can be downloaded here
The basic structure of the XML is as follows:
<locations>
<location lat="38.8561" lon="-94.6654" timezone="UTC" city="Overland Park" region="KS" country="US" zipcode="66223" offset="0" local_offset_hours="-5">
<sfc_ob>
<attribute1></attribute1>
</sfc_ob>
<daily_summaries>
<daily_summary>
<attribute2> </attribute2>
</daily_summary>
</daily_summaries>
<hourly_summaries>
<hourly_summary>
<attribute3></attribute3>
</hourly_summary>
</hourly_summaries>
</location>
</locations>
My Core Data Entities are as follows:
RESTKIT RELATED CODE
- (GLWeatherManager *)init {
self = [super init];
// setup logging
RKLogConfigureByName("RestKit/Network*", RKLogLevelTrace);
RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelTrace);
self.httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:@"http://weather.wdtinc.com"]];
[self.httpClient setDefaultHeader:@"Accept" value:RKMIMETypeXML];
[RKMIMETypeSerialization registerClass:[RKXMLReaderSerialization class] forMIMEType:@"application/xml"];
self.restKitManager = [[RKObjectManager alloc] initWithHTTPClient:self.httpClient];
self.restKitManager.managedObjectStore = [[RKManagedObjectStore alloc] initWithPersistentStoreCoordinator:[NSPersistentStoreCoordinator MR_defaultStoreCoordinator]];
[self.restKitManager.managedObjectStore createManagedObjectContexts];
// Locations
RKEntityMapping *locationMapping = [self buildMapForLocations];
RKEntityMapping *currentConditionsMapping = [self buildMapForCurrentConditions];
RKEntityMapping *dailySummariesMapping = [self buildMapForDailySummaries];
RKEntityMapping *hourlyForecastsMapping = [self buildMapForHourlyForecasts];
[locationMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"daily_summaries" toKeyPath:@"dailySummaries" withMapping:dailySummariesMapping]];
[locationMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"hourly_summaries" toKeyPath:@"hourlyForecasts" withMapping:hourlyForecastsMapping]];
[locationMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"sfc_ob" toKeyPath:@"currentConditions" withMapping:currentConditionsMapping]];
[dailySummariesMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:nil toKeyPath:@"location" withMapping:locationMapping]];
[hourlyForecastsMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:nil toKeyPath:@"location" withMapping:locationMapping]];
RKResponseDescriptor *descriptor = [RKResponseDescriptor responseDescriptorWithMapping:locationMapping pathPattern:@"/feeds/demofeeds20131031/mega.php" keyPath:@"locations.location" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
// add mapping description to objectmanager
[self.restKitManager addResponseDescriptor:descriptor];
RKResponseDescriptor *descriptor2 = [RKResponseDescriptor responseDescriptorWithMapping:currentConditionsMapping pathPattern:@"/feeds/demofeeds20131031/mega.php" keyPath:@"locations.location.sfc_ob" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[self.restKitManager addResponseDescriptor:descriptor2];
RKResponseDescriptor *descriptor3 = [RKResponseDescriptor responseDescriptorWithMapping:dailySummariesMapping pathPattern:@"/feeds/demofeeds20131031/mega.php" keyPath:@"locations.location.daily_summaries.daily_summary" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[self.restKitManager addResponseDescriptor:descriptor3];
RKResponseDescriptor *descriptor4 = [RKResponseDescriptor responseDescriptorWithMapping:hourlyForecastsMapping pathPattern:@"/feeds/demofeeds20131031/mega.php" keyPath:@"locations.location.hourly_summaries.hourly_summary"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[self.restKitManager addResponseDescriptor:descriptor4];
// start the location manager to get the current location
self.locationManager = [[CLLocationManager alloc] init];
[self.locationManager setDelegate:self];
[self.locationManager startUpdatingLocation];
self.locations = [NSMutableArray arrayWithArray:[Locations findAll]];
[self getMegaFeed];
return self;
}
- (RKEntityMapping *)buildMapForLocations {
RKEntityMapping *locationMapping = [RKEntityMapping mappingForEntityForName:@"Locations" inManagedObjectStore:self.restKitManager.managedObjectStore];
[locationMapping addAttributeMappingsFromDictionary:@{
@"lat" : @"latitude",
@"lon" : @"longitude",
@"city" : @"city",
@"region" : @"region",
@"country" : @"country",
@"zipcode" : @"zipcode",
}];
locationMapping.identificationAttributes = [NSArray arrayWithObject:@"zipcode"];
return locationMapping;
}
- (RKEntityMapping *)buildMapForCurrentConditions {
// Current Conditions
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"CurrentConditions" inManagedObjectStore:self.restKitManager.managedObjectStore];
[mapping addAttributeMappingsFromDictionary:@{
//@"stn" : @"stn",
//@"location" : @"location",
//@"stn_lat" : @"stnLatitude",
//@"stn_lon" : @"stnLongitude",
@"ob_time.text" : @"observationTime",
@"day_of_week.text" : @"dayOfWeek",
@"temp_C.text" : @"temperatureMetric",
@"temp_F.text" : @"temperatureImperial",
@"dewp_C.text" : @"dewPointMetric",
@"dewp_F.text" : @"dewPointImperial",
@"rh_pct.text" : @"relativeHumidity",
@"wnd_dir.text" : @"windDirection",
@"wnd_spd_mph.text" : @"windSpeedImperial",
@"wnd_spd_kph.text" : @"windSpeedMetric",
@"press_in.text" : @"pressureImperial",
@"press_mb.text" : @"pressureMetric",
@"wx.text" : @"conditionSummary",
@"wx_code.text" : @"conditionCode",
@"cld_cover.text" : @"cloudCover",
@"visibility_ft.text" : @"visibilityImperial",
@"visibility_m.text" : @"visibilityMetric",
@"apparent_temp_F.text" : @"feelsLikeTemperatureImperial",
@"apparent_temp_C.text" : @"feelsLikeTemperatureMetric",
@"moon_phase.text" : @"moonPhase",
@"sunrise_utc.text" : @"sunrise",
@"sunset_utc.text" : @"sunset"
}];
[mapping setIdentificationAttributes:[NSArray arrayWithObjects:@"observationTime", nil]];
return mapping;
}
- (RKEntityMapping *)buildMapForDailySummaries {
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"DailySummaries" inManagedObjectStore:self.restKitManager.managedObjectStore];
[mapping addAttributeMappingsFromDictionary:@{
@"summary_date.text" : @"date",
@"day_of_week.text" : @"dayOfWeek",
@"max_temp_F.text" : @"tempMaxImperial",
@"max_temp_C.text" : @"tempMaxMetric",
@"min_temp_F.text" : @"tempMinImperial",
@"min_temp_C.text" : @"tempMinMetric",
@"wnd_spd_mph.text" : @"windSpeedImperial",
@"wnd_spd_kph.text" : @"windSpeedMetric",
@"min_wnd_spd_mph.text" : @"windSpeedMinImperial",
@"min_wnd_spd_kph.text" : @"windSpeedMinMetric",
@"max_wnd_spd_mph.text" : @"windSpeedMaxImperial",
@"max_wnd_spd_kph.text" : @"windSpeedMaxMetric",
@"wnd_gust_mph.text" : @"windGustImperial",
@"wnd_gust_kph.text" : @"windGustMetric",
@"wnd_dir.text" : @"windDirection",
@"pop.text" : @"probabilityOfPrecipitation",
@"wx.text" : @"conditionSummary",
@"wx_code.text" : @"conditionCode",
@"text_description.text" : @"textDescription",
@"sunrise_utc.text" : @"sunrise",
@"sunset_utc.text" : @"sunset",
@"@root.locations.location.zipcode" : @"locationZipcode"
}];
mapping.identificationAttributes = [NSArray arrayWithObjects:@"date", @"locationZipcode", nil];
[mapping addConnectionForRelationship:@"location" connectedBy:@{@"locationZipcode": @"zipcode"}];
return mapping;
}
- (RKEntityMapping *)buildMapForHourlyForecasts {
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"HourlyForecasts" inManagedObjectStore:self.restKitManager.managedObjectStore];
[mapping addAttributeMappingsFromDictionary:@{
@"day_of_week_utc.text" : @"dayOfWeek",
@"time_utc.text" : @"forecastTime",
@"temp_C.text" : @"temperatureMetric",
@"temp_F.text" : @"temperatureImperial",
@"dewp_C.text" : @"dewPointMetric",
@"dewp_F.text" : @"dewPointImperial",
@"app_temp_C.text" : @"feelsLikeTemperatureMetric",
@"app_temp_F.text" : @"feelsLikeTemperatureImperial",
@"rh_pct.text" : @"relativeHumidity",
@"wx.text" : @"conditionSummary",
@"wx_code.text" : @"conditionCode",
@"day_night.text" : @"dayNight",
@"pop.text" : @"probabilityOfPrecipitation",
@"sky_cov_pct.text" : @"skyCoverPercent",
@"wnd_dir.text" : @"windDirection",
@"wnd_dir_degs.text" : @"windDirectionDegrees",
@"wnd_spd_mph.text" : @"windSpeedImperial",
@"wnd_spd_kph.text" : @"windSpeedMetric",
@"@root.locations.location.zipcode" : @"locationZipcode"
}];
mapping.identificationAttributes = [NSArray arrayWithObjects:@"forecastTime", @"locationZipcode", nil];
[mapping addConnectionForRelationship:@"location" connectedBy:@{@"locationZipcode": @"zipcode"}];
return mapping;
}
- (void)getMegaFeed {
for (Locations *location in self.locations) {
NSString *path = [NSString stringWithFormat:@"/feeds/demofeeds20131031/mega.php?ZIP=%@&UNITS=all",location.zipcode];
// fetch data
[self.restKitManager getObjectsAtPath:path
parameters:nil
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSArray *mappedObjects = [mappingResult array];
NSMutableArray *validObjectIDs = [[NSMutableArray alloc] initWithCapacity:[mappedObjects count]];
for (NSManagedObject *object in mappedObjects) {
NSManagedObjectID *moID = [object objectID];
[validObjectIDs addObject:moID];
}
[self notifyObservers:@selector(megaFeedDidFinish:location:) withObject:validObjectIDs withObject:location];
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
[REUtility showDefaultAlertWithError:error];
[RELog error:@"%s Hit error:%@", __func__, error];
}];
}
}