RestKit マッピング関係 (0.20.3) に問題があります。他の投稿を見ましたが、まだ解決策が見つかりません。私は近いと思いますが、まだ何かが欠けています。
問題
エンティティ属性は Core Data に適切にマッピングされています。ただし、エンティティに関係がある場合はマップされません。現在限界に達しているため、取り除く必要がある回避策があります。
回避策として、マッピングが成功した後、マネージド オブジェクト ID をフォアグラウンド スレッドに渡して、有効なマネージド オブジェクトをハイドレートしました。次に、オブジェクトをループして関係を確立し、Core Data に「再保存」します。
回避策の問題は、マップされた関係がidentificationAttributesパラメーターの一意のキーの一部であることです。マッピングがバックグラウンドで適切に機能していないため、既存のレコードが適切に識別されず、将来の API 呼び出しで重複エントリが発生します。
バックグラウンド
ソース XML の例は、ここからダウンロードできます。
XML の基本構造は次のとおりです。
<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>
私のコアデータエンティティは次のとおりです。
RESTKIT 関連コード
- (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:@"locations.location.daily_summaries" toKeyPath:@"dailySummaries" withMapping:dailySummariesMapping]];
[locationMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"locations.location.hourly_summaries" toKeyPath:@"hourlyForecasts" withMapping:hourlyForecastsMapping]];
[locationMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"locations.location.sfc_ob" toKeyPath:@"currentConditions" withMapping:currentConditionsMapping]];
[currentConditionsMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"locations.location" toKeyPath:@"location" withMapping:locationMapping]];
[dailySummariesMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"locations.location" toKeyPath:@"location" withMapping:locationMapping]];
[hourlyForecastsMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"locations.location" 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",
@"locations.location.zipcode.text" : @"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",
@"locations.location.zipcode.text" : @"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];
}];
}
}
アップデート
@ワイン
ご指摘のとおり、コードを変更してみました。現在の状態オブジェクトは場所を適切に受け取っているように見えますが、1 対 1 の関係です。他のものは 1 対多で、まだ正しくマッピングされていません。テストされたコードの変更は以下のとおりです。
[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]];
[currentConditionsMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"location" toKeyPath:@"location" withMapping:locationMapping]];
[dailySummariesMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"location" toKeyPath:@"location" withMapping:locationMapping]];
[hourlyForecastsMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"location" toKeyPath:@"location" withMapping:locationMapping]];