I am using RestKit for my rest implementation. I fetch the data from the server and upon receiving it, I call saveToPersistentStore which is successful as well but when I turn off internet and load that page again, it doesnt find any data in the store.
Here is the initialization flow -
NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"Storage" ofType:#"momd"]];
// NOTE: Due to an iOS 5 bug, the managed object model returned is immutable.
NSManagedObjectModel *managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] mutableCopy];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
// Initialize the Core Data stack
[managedObjectStore createPersistentStoreCoordinator];
NSPersistentStore __unused *persistentStore = [managedObjectStore addInMemoryPersistentStore:&error];
NSAssert(persistentStore, #"Failed to add persistent store: %#", error);
[managedObjectStore createManagedObjectContexts];
// Set the default store shared instance
[RKManagedObjectStore setDefaultStore:managedObjectStore];
I use the mainQueue for everythng -
controller.managedObjectContext = managedObjectStore.mainQueueManagedObjectContext;
This is the function which does the fetch:
[CMLRKSharedManager getNewsForUser:userProfile
urlParams:dict
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
[self.tableView reloadData];
NSLog(#"Fetched object count %lu %# %#", (unsigned long)self.fetchedResultsController.fetchedObjects.count , self.fetchedResultsController, self.fetchedResultsController.managedObjectContext);
NSManagedObjectContext *managedObjCtx = [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext;
NSError *executeError = nil;
if(![managedObjCtx saveToPersistentStore:&executeError]) {
NSLog(#"Failed to save to data store");
}
else
NSLog(#"SAVE SUCCESSFUL");
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
[self.tableView reloadData];
}];
}
Output:
2014-04-02 22:24:01.547 Fetched object count 4
2014-04-02 22:24:01.555 SAVE SUCCESSFUL
Now I kill the app, turn off wifi and launch app again. The table is now blank and I see that the fetchResultsController is seeing 0 items in console log
Apr 2 22:24:36 : In numberOfRows Fetched object count 0
I'm not sure why...
#wain I update the code as follows -
NSError *error = nil;
NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"Cover" ofType:#"momd"]];
// NOTE: Due to an iOS 5 bug, the managed object model returned is immutable.
NSManagedObjectModel *managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] mutableCopy];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
// Initialize the Core Data stack
[managedObjectStore createPersistentStoreCoordinator];
Commented these 2 lines
// NSPersistentStore __unused *persistentStore = [managedObjectStore addInMemoryPersistentStore:&error];
// NSAssert(persistentStore, #"Failed to add persistent store: %#", error);
//
Instead added the sqlite db but now the app crashed
BOOL success = RKEnsureDirectoryExistsAtPath(RKApplicationDataDirectory(), &error);
if (! success) {
RKLogError(#"Failed to create Application Data Directory at path '%#': %#", RKApplicationDataDirectory(), error);
}
NSString *path = [RKApplicationDataDirectory() stringByAppendingPathComponent:#"Store.sqlite"];
NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:path fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
if (! persistentStore) {
RKLogError(#"Failed adding persistent store at path '%#': %#", path, error);
}
From your description, the issue is that you only have an in-memory store:
[managedObjectStore addInMemoryPersistentStore:&error];
so nothing is saved to disk. You should change this so that you are using an SQLite store. This will save data to disk and generally work better in most usage scenarios.
Related
My iOS app embed a .sqlite database containing a list of cities with postal codes and geolocation informations.
I initialize my Core Data stack like this:
- (void)initializeCoreData {
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"cities" withExtension:#"momd"];
NSManagedObjectModel *mom = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
NSAssert(mom != nil, #"Error initializing Managed Object Model");
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[moc setPersistentStoreCoordinator:psc];
[self setManagedObjectContext:moc];
NSURL *storeURL = [[NSBundle mainBundle] URLForResource:#"cities" withExtension:#"sqlite"];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
NSError *error = nil;
NSPersistentStoreCoordinator *psc = [[self managedObjectContext] persistentStoreCoordinator];
NSDictionary * options = #{NSSQLitePragmasOption:#{#"journal_mode":#"DELETE"}};
NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error];
NSAssert(store != nil, #"Error initializing PSC: %#\n%#", [error localizedDescription], [error userInfo]);
});
}
As you can see, I don't copy the database to the application directory, but read it directly from the application bundle.
I only have to read this database, so is it safe to initialise Core Data like i do, or should I copy the database to the application directory before?
Yes it is safe but I would add the "read-only" option as well so that you can protect yourself from potential mistakes:
NSMutableDictionary *options = [NSMutableDictionary dictionary];
options[NSReadOnlyPersistentStoreOption] = #YES;
options[NSSQLitePragmasOption] = #{#"journal_mode":#"DELETE"};
ANSWERED Q2 by FIXING Q1, FEEL FREE TO EXPLAIN IF YOU KNOW WHY
Full disclosure, I dabble in Objective C so this could be painfully obvious but I could not find similar problems or more accurately answers to similar problems.
I'm writing a very simple app and I am learning about core data and I have a couple of issues
Q.1 Has been answered so here is Q.2
Q2. When fetching from Core Data I retrieve empty objects?
With the Data Controller being the same as the above question I call
- (NSMutableArray*) returnTimers
{
[self initilizeDataLayer];
NSMutableArray *listOfTimers = [[NSMutableArray alloc]init];
NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:#"Timer"];
NSError *error = nil;
NSArray *listOfTimersRaw = [myDataController.managedObjectContext executeFetchRequest:request error:&error];
if (error != nil) {
//Deal with failure
}
else {
//Deal with success
listOfTimers = [NSMutableArray arrayWithArray:listOfTimersRaw];
}
return listOfTimers;
}
This correctly retrieves 2 objects but they are empty?
Again this is experimentation so may be arse ways but I have to learn somehow.....Any help would be greatly appreciated.
BELOW HERE HAS BEEN ANSWERED THANKS
Q1. Saving Objects to Core Data creates a blank row in sqlite db - Why?
I have set up a DataController Object like in the Apple Docs
- (void)initializeCoreData
{
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"WorkRestWork" withExtension:#"momd"];
NSManagedObjectModel *mom = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
NSAssert(mom != nil, #"Error initializing Managed Object Model");
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[moc setPersistentStoreCoordinator:psc];
[self setManagedObjectContext:moc];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *documentsURL = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *storeURL = [documentsURL URLByAppendingPathComponent:#"WorkRestWork.sqlite"];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
NSError *error = nil;
NSPersistentStoreCoordinator *psc = [[self managedObjectContext] persistentStoreCoordinator];
NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:_options error:&error];
NSAssert(store != nil, #"Error initializing PSC: %#\n%#", [error localizedDescription], [error userInfo]);
});
}
Then I call this method
- (BOOL) addTimerWithName: (NSString*)timerName
{
[self initilizeDataLayer];
Timer *newTimer = [NSEntityDescription insertNewObjectForEntityForName:#"Timer" inManagedObjectContext:myDataController.managedObjectContext];
newTimer = [NSEntityDescription insertNewObjectForEntityForName:#"Timer" inManagedObjectContext:myDataController.managedObjectContext];
newTimer.name = timerName;
[myDataController.managedObjectContext insertObject:newTimer];
NSError *error = nil;
if ([myDataController.managedObjectContext save:&error] == NO) {
NSAssert(NO, #"Error saving context: %#\n%#", [error localizedDescription], [error userInfo]);
return NO;
}
return YES;
}
And Finally
[cdh addTimerWithName:#"A TEST"];
When I check check the .sqlite file using Base there are 2 rows, one with "A TEST" as the Name and the other empty which is confusing me
Ad Q1.
You are inserting two timers here. However you assign a name just to the second one. First one has no name cause you never set it. Here's your code with some comments:
// Inserts first timer
Timer *newTimer = [NSEntityDescription insertNewObjectForEntityForName:#"Timer" inManagedObjectContext:myDataController.managedObjectContext];
// Inserts second timer
newTimer = [NSEntityDescription insertNewObjectForEntityForName:#"Timer" inManagedObjectContext:myDataController.managedObjectContext];
// Sets name of the second timer
newTimer.name = timerName;
I am trying to remove the iCloud sync capability from a NSPersistentStore in my app. The migration does not return any errors, however, the data from the iCloud-enabled persistent store disappears after the migration.
The following is my relevant code:
-(void)setUpCoreDataStackWithICloud
{
//Set up the model and context.
self.model = [NSManagedObjectModel mergedModelFromBundles:nil];
self.context = [[NSManagedObjectContext alloc]init];
[self.context setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
if ([self isiCloudEnabledOnThisDevice]==YES) {
[self setUpiCloud];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setValue:[NSNumber numberWithBool:YES] forKey:#"lastLaunchOfAppiCloudWasEnabled"];
}else self.psc = [self persistentStoreCoordinatorForUserWithoutiCloudEnabled];
[self.context setPersistentStoreCoordinator:self.psc];
}
-(NSPersistentStoreCoordinator*)persistentStoreCoordinatorForUserThatAlreadyMigratedToiCloud
{
NSLog(#"persistentStoreCoordinatorForUserThatAlreadyMigratedToiCloud called");
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc]initWithManagedObjectModel:self.model];
NSError *error = nil;
NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:[self newStoreURL]
options:[self iCloudStoreOptions]
error:&error];
if (!store) {
NSLog(#"failed to add store with error %#", [error localizedDescription]);
}else NSLog(#"successfully added store for user that has already migrated to iCloud");
NSDictionary *migrateOptions = #{ NSPersistentStoreRemoveUbiquitousMetadataOption : #YES,
NSMigratePersistentStoresAutomaticallyOption : #YES,
NSInferMappingModelAutomaticallyOption : #YES};
NSError *migrationError = nil;
NSPersistentStore *migratedStore = [psc migratePersistentStore:store toURL:[self testNewestStoreURL] options:migrateOptions withType:NSSQLiteStoreType error:&migrationError];
if (migrationError) {
NSLog(#"Migration from iCloud enabled persistent store was unsuccessful. %#", migrationError.localizedDescription);
}else NSLog(#"The migration from the iCloud enabled persistent store was successful");
NSLog(#"The migrated store is %#", migratedStore);
return psc;
}
-(NSURL*)testNewestStoreURL{
NSString *path = [self itemArchivePathWithAppendedString:#"test.data"];
NSURL *storeURL = [NSURL fileURLWithPath:path];
return storeURL;
}
-(NSURL*)newStoreURL
{
NSString *path = [self itemArchivePathWithAppendedString:#"cloudstore.data"];
NSURL *storeURL = [NSURL fileURLWithPath:path];
return storeURL;
}
The reason it won't work is because I am also doing a schema migration using a mapping model. iCloud doesn't support this according to the docs. Even though I am trying to get away from iCloud, it does not allow access to the data when there is a schema migration involved.
I have iPad Application with three view controllers in uitabbarcontroller. In each view controller, I am calling different web service and mapping them to core data entity by using RestKit and then displaying the data in uitableview with nsfetchedresultscontroller. More information about my implementation is here.
First time when I load the data, the data is loaded and mapped correctly on every view controller but in one of the view controller when I try to reload the data in view controller-2 after loading data in view controller-3, the application is crashing with the below error:
2015-11-07 00:17:45.205 PharmacyStockTake[193:3854] T restkit.network:RKResponseMapperOperation.m:504 Mapping HTTP response to nil target object...
2015-11-07 00:17:45.206 PharmacyStockTake[193:3854] I restkit.core_data:RKInMemoryManagedObjectCache.m:94 Caching instances of Entity 'RackStockTakeStatus' by attributes 'stockTakeLocId'
2015-11-07 00:17:45.213 PharmacyStockTake[193:3854] * Assertion failure in -[RKEntityByAttributeCache addObjects:completion:], /Users/saif/Documents/iDev/ComPrjs/PharmacyStockTake/Pods/RestKit/Code/CoreData/RKEntityByAttributeCache.m:333
2015-11-07 00:17:45.214 PharmacyStockTake[193:3854] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Cannot add object with entity 'RackStockTakeStatus' to cache for entity of 'RackStockTakeStatus''
*** First throw call stack:
(0x2201585b 0x33a5adff 0x22015731 0x22da6ddb 0x14b67b 0x21d9034b 0x21d9022d 0x14b175 0x15a9e1 0x21d9034b 0x21d9022d 0x15a71d 0x161327 0x17bfb9 0x17a769 0x220091e9 0x21f8bbdb 0x17a65b 0x17c21b 0x17c9cd 0x17d271 0x22d283cf 0x1c3925 0x21d9034b 0x422d03 0x42c4fb 0x21d90247 0x1c26bb 0x1c072d 0x22d283cf 0x22dd682d 0x42d61b 0x425f53 0x42eb0f 0x42e961 0x34314e0d 0x343149fc)
libc++abi.dylib: terminating with uncaught exception of type NSException
Here is my code:
Initializing RestKit in AppDelegate:
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];
RestKit Initialization and configuration code in View-Controller-2:
-(void)initTakeStockRestKit
{
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:#"/stocktake/stocktake/:id/usr/:id/locwithstatus"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)
];
[objectManager addResponseDescriptor:rackStockTakeStatusListResponseDescriptor];
}
Data loading on View-Controller-2:
NSString *requestPath = [NSString stringWithFormat:#"/stocktake/stocktake/%#/usr/1/locwithstatus",[defaults objectForKey:#"loggedInUserSelectedStoreId"]];
[[RKObjectManager sharedManager]
getObjectsAtPath:requestPath
parameters:nil
success: ^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSManagedObjectContext *context = [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"RackStockTakeStatus" inManagedObjectContext:context];
[request setEntity:entity];
NSError *error;
[context executeFetchRequest:request error:&error];
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
}
[self.tableView reloadData];
NSLog(#"requestDataItemsForStore - Mapping Success");
}
failure: ^(RKObjectRequestOperation *operation, NSError *error) {
RKLogError(#"Load failed with error: %#", error);
NSLog(#"requestDataItemsForStore - Loading Failed");
}
];
RestKit Initialization and configuration code in View-Controller-3:
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:itemsByLocationRequestPath];
NSDictionary *argsDict = nil;
BOOL match = [pathMatcher matchesPath:[URL relativePath] tokenizeQueryStrings:NO parsedArguments:&argsDict];
if (match) {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"ItemsByLocation"];
return fetchRequest;
}
return nil;
}];
RKEntityMapping *itemsByLocationListMapping = [RKEntityMapping mappingForEntityForName:#"ItemsByLocation" inManagedObjectStore:managedObjectStore];
itemsByLocationListMapping.identificationAttributes = #[#"itemId"];
[itemsByLocationListMapping addAttributeMappingsFromDictionary:
#{
#"itemId" : #"itemId",
#"itemName" : #"itemName",
#"itemCode" : #"itemCode",
#"uomCode" : #"uomCode",
#"locId" : #"locId",
#"locName" : #"locName",
#"subLocId" : #"subLocId",
#"subLocName" : #"subLocName",
#"storeItemId" : #"storeItemId",
#"stockTakeQtyId" : #"stockTakeQtyId",
#"countedTime" : #"countedTime",
#"countQty" : #"countQty",
#"removed" : #"removed",
#"remarks" : #"remarks"
}
];
RKResponseDescriptor *itemsByLocationListResponseDescriptor =
[RKResponseDescriptor responseDescriptorWithMapping:itemsByLocationListMapping
method:RKRequestMethodGET
pathPattern:#"/stocktake/stocktake/:id/loc/:id/usr/:id/items"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)
];
[objectManager addResponseDescriptor:itemsByLocationListResponseDescriptor];
Data Loading on View Controller-3:
[[RKObjectManager sharedManager]
getObjectsAtPath:itemsByLocationRequestPath
parameters:nil
success: ^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSManagedObjectContext *context = [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"ItemsByLocation"];
NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:#"subLocName" ascending:YES];
fetchRequest.sortDescriptors = #[descriptor];
NSError *error = nil;
[context executeFetchRequest:fetchRequest error:&error];
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
}
[self.tableView reloadData];
NSLog(#"requestDataItemsForStore - Mapping Success");
}
failure: ^(RKObjectRequestOperation *operation, NSError *error) {
RKLogError(#"Load failed with error: %#", error);
NSLog(#"requestDataItemsForStore - Loading Failed");
}
];
Can you help to resolve this issue?
This is a threading and / or a multiple context issue. Your entity cache is setup with one context but then you're using it from a different context (which is why the entities have the same name but are different).
It's possible that this is because you're setting up multiple different core data stacks (it looks like it from the code samples). In this case you should factor out all of the core data stack and mapping so creation code into a new class - a data manager. Pass the data manager to each of the view controllers so they can use it to download the content they need.
If it's a threading issue then turning on core data threading exceptions could help you find the cause.
Note that using FRCs in each view controller is fine, but that you should only have one main thread context that you set them all up with.
In my RestKit application, I need to store an email and password in CoreData (I know about NSUserDefaults, but I have other user properties I need stored in CoreData as well).
NSManagedObjectContext *context = [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext;
NSError *error = nil;
NSEntityDescription *userEntity = [NSEntityDescription
entityForName:#"User" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:userEntity];
NSArray *users = [context executeFetchRequest:request error:&error];
return (User *)[users firstObject];
This user object is always null at the application launch.
Then, when the user enters his/her information, I save it like so:
- (void)cacheEmailAndPassword:(NSDictionary *)credentials {
NSManagedObjectContext *context = [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext;
NSError *error = nil;
NSEntityDescription *userEntity = [NSEntityDescription
entityForName:#"User" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:userEntity];
NSArray *users = [context executeFetchRequest:request error:&error]; //should be a single object array for now
User *user = (User *)[users firstObject];
user.email = [credentials objectForKey:#"email"];
user.password = [credentials objectForKey:#"password"];
}
Afterwards, when I try to get the User data from the first function, it returns with all the information. However, when I relaunch the program, the data is null again. Why is this data not persisting between application launches?
EDIT: In case I screwed something up in my initialization, I've pasted it below:
NSError *error = nil;
NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"Model" ofType:#"momd"]];
// NOTE: Due to an iOS 5 bug, the managed object model returned is immutable.
NSManagedObjectModel *managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] mutableCopy];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
// Initialize the Core Data stack
[managedObjectStore createPersistentStoreCoordinator];
// Configure the object manager
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:BASE_URL]];
objectManager.managedObjectStore = managedObjectStore;
NSPersistentStore __unused *persistentStore = [managedObjectStore addInMemoryPersistentStore:&error];
NSAssert(persistentStore, #"Failed to add persistent store: %#", error);
[managedObjectStore createManagedObjectContexts];
// Set the default store shared instance
[RKManagedObjectStore setDefaultStore:managedObjectStore];
[RKObjectManager setSharedManager:objectManager];
This is how to add persistent storage
NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"storage" ofType:#"momd"]];
// NOTE: Due to an iOS 5 bug, the managed object model returned is immutable.
NSManagedObjectModel *managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] mutableCopy];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
BOOL success = RKEnsureDirectoryExistsAtPath(RKApplicationDataDirectory(), &error);
if (! success) {
RKLogError(#"Failed to create Application Data Directory at path '%#': %#", RKApplicationDataDirectory(), error);
}
NSString *path = [RKApplicationDataDirectory() stringByAppendingPathComponent:#"MyStore.sqlite"];
NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:path fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
if (! persistentStore) {
RKLogError(#"Failed adding persistent store at path '%#': %#", path, error);
}
And whenever you are done with the changes, dont forget to save
if(![self.managedObjectContext saveToPersistentStore:&error]){
You never create a user anywhere in the code you posted. You first fetch all users - and none come back. Then you fetch them again - why do you expect that there is one now? Did you check if the user is nil the second time as well?
What you need to to is to insert a new User object if there is none:
User *user = [NSEntityDescription insertNewObjectForEntityForName:#"User"
inManagedObjectContext:context];
// configure the user
[context save:nil];