Reputation: 833
I had read this document but it does not show any example solution for this problem: RKManagedObjectRequestOperation Class Reference
I want to use several addFetchRequestBlock methods to remove orphaned objects from the same API call I am making. When I remove some record in server side, the parent objects work fine. However, Restkit does not remove those nested objects.
My API JSON output - /regions :
(Region has many Districts and Buildings)
(District has many Buildings)
{
"regions": [
{
"id": 1,
"name": "region A",
"districts": [
{
"id": 1,
"region_id": 1,
"name": "district A",
"buildings": [
{
"id": 1,
"region_id": 1,
"district_id": 1,
"name": "building A"
},
{
"id": 2,
"region_id": 1,
"district_id": 1,
"name": "building B"
}
]
},
{
"id": 2,
"region_id": 1,
"name": "district B",
"buildings": []
},
{
"id": 3,
"region_id": 1,
"name": "district C",
"buildings": []
}
]
},
{
"id": 2,
"name": "region B",
"districts": [
{
"id": 4,
"region_id": 2,
"name": "district D",
"buildings": [
{
"id": 3,
"region_id": 2,
"district_id": 4,
"name": "building C"
}
]
},
{
"id": 2,
"region_id": 1,
"name": "district E",
"buildings": []
}
]
}
]
}
My core data model:
My Object Mapping and addFetchRequestBlock:
(I am trying to use 3 addFetchRequestBlock for deleting orphaned objects; Region, District and Building when the URL path is called)
(1, Restkit deletes those orphaned Region objects correctly)
(2. The deleted objects in District and Building are still in client side)
- (void) setupRegionMappings
{
// Object Mappings
// -------------------------------------------------
// Get RKObjectManager singleton
RKObjectManager *manager = [RKObjectManager sharedManager];
// Get default managed object store
RKManagedObjectStore *managedObjectStore = [RKManagedObjectStore defaultStore];
// Create the RKObjectMapping mapping for our object class:
RKEntityMapping *buildingMapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([Building class])
inManagedObjectStore:managedObjectStore];
[buildingMapping addAttributeMappingsFromDictionary:@{
@"id": @"buildingID",
@"district_id": @"districtID",
@"region_id": @"regionID",
@"name": @"name",
@"name_zh": @"nameZh",
@"name_cn": @"nameCn",
@"name_en": @"nameEn"
}];
// Identify the object in database
buildingMapping.identificationAttributes = @[@"buildingID"];
// Establish the connection for relation between attributes in core data
[buildingMapping addConnectionForRelationship:@"region" connectedBy:@{@"regionID": @"regionID"}];
[buildingMapping addConnectionForRelationship:@"district" connectedBy:@{@"districtID": @"districtID"}];
[buildingMapping addConnectionForRelationship:@"shops" connectedBy:@{@"buildingID": @"buildingID"}];
// Create the RKObjectMapping mapping for our object class:
RKEntityMapping *districtMapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([District class])
inManagedObjectStore:managedObjectStore];
[districtMapping addAttributeMappingsFromDictionary:@{
@"id": @"districtID",
@"region_id": @"regionID",
@"name": @"name",
@"name_zh": @"nameZh",
@"name_cn": @"nameCn",
@"name_en": @"nameEn"
}];
// Identify the object in database
districtMapping.identificationAttributes = @[@"districtID"];
// Establish the connection for relation between attributes in core data
[districtMapping addConnectionForRelationship:@"region" connectedBy:@{@"regionID": @"regionID"}];
[districtMapping addConnectionForRelationship:@"buildings" connectedBy:@{@"districtID": @"districtID"}];
[districtMapping addConnectionForRelationship:@"shops" connectedBy:@{@"districtID": @"districtID"}];
// Define the relationship mapping on json
[districtMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"buildings"
toKeyPath:@"buildings"
withMapping:buildingMapping]];
// Create the RKObjectMapping mapping for our object class:
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([Region class])
inManagedObjectStore:managedObjectStore];
[mapping addAttributeMappingsFromDictionary:@{
@"id": @"regionID",
@"name": @"name",
@"name_zh": @"nameZh",
@"name_cn": @"nameCn",
@"name_en": @"nameEn"
}];
// Identify the object in database
mapping.identificationAttributes = @[@"regionID"];
// Establish the connection for relation between attributes in core data
[mapping addConnectionForRelationship:@"districts" connectedBy:@{@"regionID": @"regionID"}];
[mapping addConnectionForRelationship:@"buildings" connectedBy:@{@"regionID": @"regionID"}];
[mapping addConnectionForRelationship:@"shops" connectedBy:@{@"regionID": @"regionID"}];
// Define the relationship mapping on json
[mapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"districts"
toKeyPath:@"districts"
withMapping:districtMapping]];
// The mapping will be triggered if a response status code is anything in 2xx
NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);
// Put it all together in response descriptor (for a GET request method)
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping
method:RKRequestMethodGET
pathPattern:@"regions"
keyPath:@"regions"
statusCodes:statusCodes];
// Add response descriptor to our shared manager
[manager addResponseDescriptor:responseDescriptor];
// Routings
// -------------------------------------------------
// Route for list of type objects
RKRoute *indexRoute = [RKRoute routeWithName:@"regions" pathPattern:@"regions" method:RKRequestMethodGET];
indexRoute.shouldEscapePath = YES;
// Add defined routes to the Object Manager router
[manager.router.routeSet addRoutes:@[indexRoute]];
// Making Consistency
// -------------------------------------------------
// Deleting orphaned objects
// Define Fetch request to trigger on specific url
[manager addFetchRequestBlock:^NSFetchRequest *(NSURL *URL) {
// Create a path matcher
RKPathMatcher *pathMatcher = [RKPathMatcher pathMatcherWithPattern:@"regions"];
// Dictionary to store request arguments
NSDictionary *argsDict = nil;
// Match the URL with pathMatcher and retrieve arguments
BOOL match = [pathMatcher matchesPath:[URL relativePath] tokenizeQueryStrings:NO parsedArguments:&argsDict];
// If url matched, create NSFetchRequest
if (match) {
NSFetchRequest *fetchRequest = [Region MR_requestAllSortedBy:@"regionID" ascending:YES];
return fetchRequest;
}
return nil;
}];
[manager addFetchRequestBlock:^NSFetchRequest *(NSURL *URL) {
// Create a path matcher
RKPathMatcher *pathMatcher = [RKPathMatcher pathMatcherWithPattern:@"regions"];
// Dictionary to store request arguments
NSDictionary *argsDict = nil;
// Match the URL with pathMatcher and retrieve arguments
BOOL match = [pathMatcher matchesPath:[URL relativePath] tokenizeQueryStrings:NO parsedArguments:&argsDict];
// If url matched, create NSFetchRequest
if (match) {
NSFetchRequest *fetchRequest = [District MR_requestAllSortedBy:@"districtID" ascending:YES];
return fetchRequest;
}
return nil;
}];
[manager addFetchRequestBlock:^NSFetchRequest *(NSURL *URL) {
// Create a path matcher
RKPathMatcher *pathMatcher = [RKPathMatcher pathMatcherWithPattern:@"regions"];
// Dictionary to store request arguments
NSDictionary *argsDict = nil;
// Match the URL with pathMatcher and retrieve arguments
BOOL match = [pathMatcher matchesPath:[URL relativePath] tokenizeQueryStrings:NO parsedArguments:&argsDict];
// If url matched, create NSFetchRequest
if (match) {
NSFetchRequest *fetchRequest = [Building MR_requestAllSortedBy:@"buildingID" ascending:YES];
return fetchRequest;
}
return nil;
}];
}
How can I sync these nested entities when I am making the same API call once? Great Thanks!
Upvotes: 0
Views: 462
Reputation: 119031
Only one fetch request block will be used. The correct approach to solve your problem would be to use the Core Data deletion rules on your relationships such that a deletion of a Region
results in a cascade
deletion of the associated District
s and Building
s.
The fetch request blocks you show in the question delete everything that wasn't received in the last request. Because you are using identificationAttributes
and 1:many relationships it is possible for the received response to reconnect existing District
s and Building
s to a new Region
before the Region
is deleted, so the cascade would not apply to them.
Upvotes: 1
Reputation: 833
After I delete those addConnectionForRelationship functions. It works perfectly...
Upvotes: 0