I am developing an iPad App. I have three different view controllers wherein I call three different web services using RestKit and map them to core data entity. I display the data in all view controllers by using NSFetchedResultsController.
First time when I try to load the data, it works correctly on all the view controllers. Later when I come up to the first view controller and try to load the data, it fails. I feel I am messing something.
Here is my code. In AppDelegate, I initialize RestKit:
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:baseURL];
//[RKObjectManager setSharedManager:objectManager];
[RKObjectManager setSharedManager:objectManager];
// Initialize managed object model from bundle
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
// Initialize managed object store
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
objectManager.managedObjectStore = managedObjectStore;
// Complete Core Data stack initialization
[managedObjectStore createPersistentStoreCoordinator];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"StockTakeDB.sqlite"];
NSString *seedPath = [[NSBundle mainBundle] pathForResource:@"StoreItemsDB" ofType:@"sqlite"];
NSError *error;
NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:seedPath withConfiguration:nil options:nil error:&error];
NSAssert(persistentStore, @"Failed to add persistent store with error: %@", error);
// Create the managed object contexts
[managedObjectStore createManagedObjectContexts];
// Configure a managed object cache to ensure we do not create duplicate objects
managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
viewStockRestKitManagedObjectContext = [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext;
takeStockRestKitManagedObjectContext = [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext;
takeStockItemsByLocationRestKitManagedObjectContext = [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext;
In the first view controller, I setup mapping and other required things as shown below:
RKObjectManager *objectManager = [RKObjectManager sharedManager];
// Initialize managed object model from bundle
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
// Initialize managed object store
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
objectManager.managedObjectStore = managedObjectStore;
//Complete Core Data stack initialization
[managedObjectStore createPersistentStoreCoordinator];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"StockTakeDB.sqlite"];
NSString *seedPath = [[NSBundle mainBundle] pathForResource:@"StoreItemsDB" ofType:@"sqlite"];
NSError *error;
NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:seedPath withConfiguration:nil options:nil error:&error];
NSAssert(persistentStore, @"Failed to add persistent store with error: %@", error);
// Create the managed object contexts
[managedObjectStore createManagedObjectContexts];
// Configure a managed object cache to ensure we do not create duplicate objects
managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
[objectManager addFetchRequestBlock:^NSFetchRequest *(NSURL *URL) {
RKPathMatcher *pathMatcher = [RKPathMatcher pathMatcherWithPattern:viewStockRequestPath];
NSDictionary *argsDict = nil;
BOOL match = [pathMatcher matchesPath:[URL relativePath] tokenizeQueryStrings:NO parsedArguments:&argsDict];
if (match) {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"StoreItems"];
return fetchRequest;
}
return nil;
}];
RKEntityMapping *storeItemsListMapping = [RKEntityMapping mappingForEntityForName:@"StoreItems" inManagedObjectStore: managedObjectStore];
storeItemsListMapping.identificationAttributes = @[@"itemId"];
[storeItemsListMapping addAttributeMappingsFromDictionary:
@{
@"itemId" : @"itemId",
@"itemName" : @"itemName",
@"itemCode" : @"itemCode",
@"uomCode" : @"uomCode",
@"locId" : @"locId",
@"locName" : @"locName",
@"subLocId" : @"subLocId",
@"subLocName" : @"subLocName",
@"storeItemId" : @"storeItemId"
}
];
RKResponseDescriptor *storeItemsListResponseDescriptor =
[RKResponseDescriptor responseDescriptorWithMapping:storeItemsListMapping
method:RKRequestMethodGET
pathPattern:nil
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:storeItemsListResponseDescriptor];
Then I am getting the Objects:
[[RKObjectManager sharedManager]
getObjectsAtPath:viewStockRequestPath
parameters:nil
success: ^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(@"requestDataItemsForStore - Mapping Success");
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"StoreItems"];
NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:@"locName" ascending:YES];
fetchRequest.sortDescriptors = @[descriptor];
NSError *error = nil;
[[appDelegate viewStockRestKitManagedObjectContext] executeFetchRequest:fetchRequest error:&error];
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
}
[self.tableView reloadData];
}
failure: ^(RKObjectRequestOperation *operation, NSError *error) {
RKLogError(@"Load failed with error: %@", error);
NSLog(@"requestDataItemsForStore - Loading Failed");
}
];
and here is the fetchedresultscontroller code:
- (NSFetchedResultsController *)fetchedResultsController {
if (viewStockFetchedResultsController != nil) {
return viewStockFetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"StoreItems" inManagedObjectContext:[appDelegate viewStockRestKitManagedObjectContext]];
[fetchRequest setEntity:entity];
NSSortDescriptor *rackNameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"locName" ascending:YES];
NSArray *sortDescriptors = @[rackNameDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
viewStockFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[appDelegate viewStockRestKitManagedObjectContext] sectionNameKeyPath:@"locName" cacheName:nil];
viewStockFetchedResultsController.delegate = self;
return viewStockFetchedResultsController;
}
In other view controller, I am calling different web service and mapping to different core data entity:
Here is the code in other view controller:
takeStockLocationWithStatusRequestPath = @"/stocktake/stocktake/1/usr/1/locwithstatus";
RKObjectManager *objectManager = [RKObjectManager sharedManager];
[[NSURLCache sharedURLCache] removeAllCachedResponses];
// Initialize managed object model from bundle
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
// Initialize managed object store
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
objectManager.managedObjectStore = managedObjectStore;
// Complete Core Data stack initialization
[managedObjectStore createPersistentStoreCoordinator];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"StockTakeDB.sqlite"];
NSString *seedPath = [[NSBundle mainBundle] pathForResource:@"StoreItemsDB" ofType:@"sqlite"];
NSError *error;
NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:seedPath withConfiguration:nil options:nil error:&error];
NSAssert(persistentStore, @"Failed to add persistent store with error: %@", error);
// Create the managed object contexts
[managedObjectStore createManagedObjectContexts];
// Configure a managed object cache to ensure we do not create duplicate objects
managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
[objectManager addFetchRequestBlock:^NSFetchRequest *(NSURL *URL) {
RKPathMatcher *pathMatcher = [RKPathMatcher pathMatcherWithPattern:takeStockLocationWithStatusRequestPath];
NSDictionary *argsDict = nil;
BOOL match = [pathMatcher matchesPath:[URL relativePath] tokenizeQueryStrings:NO parsedArguments:&argsDict];
if (match) {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"RackStockTakeStatus"];
return fetchRequest;
}
return nil;
}];
RKEntityMapping *rackStockTakeStatusListMapping = [RKEntityMapping mappingForEntityForName:@"RackStockTakeStatus" inManagedObjectStore:managedObjectStore];
rackStockTakeStatusListMapping.identificationAttributes = @[@"stockTakeLocId"];
[rackStockTakeStatusListMapping addAttributeMappingsFromDictionary:
@{
@"stockTakeLocId" : @"stockTakeLocId",
@"stockTakeUuid" : @"stockTakeUuid",
@"locId" : @"locId",
@"locName" : @"locName",
@"status" : @"status",
@"stockTakeByUser" : @"stockTakeByUser",
@"stockTakeByUserId" : @"stockTakeByUserId",
@"beginTime" : @"beginTime",
@"percentCompleted" : @"percentCompleted"
}
];
RKResponseDescriptor *rackStockTakeStatusListResponseDescriptor =
[RKResponseDescriptor responseDescriptorWithMapping:rackStockTakeStatusListMapping
method:RKRequestMethodGET
pathPattern:nil
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)
];
[objectManager addResponseDescriptor:rackStockTakeStatusListResponseDescriptor];
then getting the objects:
NSString *requestPath = [NSString stringWithFormat:@"/stocktake/stocktake/%@/usr/1/locwithstatus",[defaults objectForKey:@"loggedInUserSelectedStoreId"]];
[[RKObjectManager sharedManager]
getObjectsAtPath:requestPath
parameters:nil
success: ^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"RackStockTakeStatus" inManagedObjectContext:[appDelegate takeStockRestKitManagedObjectContext]];
[request setEntity:entity];
NSError *error;
[[appDelegate takeStockRestKitManagedObjectContext] executeFetchRequest:request error:&error];
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
}
[self.tableView reloadData];
NSLog(@"requestDataItemsForStore - Mapping Success");
}
failure: ^(RKObjectRequestOperation *operation, NSError *error) {
RKLogError(@"Load failed with error: %@", error);
NSLog(@"requestDataItemsForStore - Loading Failed");
}
];
FetchedResultsController code:
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"RackStockTakeStatus" inManagedObjectContext:[appDelegate takeStockRestKitManagedObjectContext]];
[fetchRequest setEntity:entity];
NSSortDescriptor *rackNameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"locName" ascending:YES];
NSArray *sortDescriptors = @[rackNameDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[appDelegate takeStockRestKitManagedObjectContext] sectionNameKeyPath:nil cacheName:nil];
fetchedResultsController.delegate = self;
return fetchedResultsController;
}
Can you help me to identify the issue?
The problem is that all of your response descriptors path patterns are nil. This means that all of the response descriptors will be applied to all responses. RestKit will create instances of whichever entity it's told to for a matching descriptor, it doesn't matter how many of the keys in the descriptor are found to map. So, you'll get a lot of partially populated instances of the wrong type created.
To fix, set the path pattern of each response descriptor to match the request URL path.