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];
Related
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.
I have an entity Country that includes an attribute downloaded which has a default value of 0, and is NOT mapped by RestKit. I want to be able to group my tableView into sections based on this downloaded attribute. Everything works as expected, until I try to programmatically change the downloaded value myself. Code below:
Where I'm trying to set the value - my context is the mainQueueManagedObjectContext that has been passed to the controller through the AppDelegate.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
Country *country = [self.fetchedResultsController objectAtIndexPath:indexPath];
[FGDataCalls downloadFieldGuide:country.countryId];
//Set up to get the thing you want to update
NSFetchRequest * request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"Country" inManagedObjectContext:self.managedObjectContext]];
[request setPredicate:[NSPredicate predicateWithFormat:#"countryId = %#", country.countryId]];
NSError *error = nil;
country = [[self.managedObjectContext executeFetchRequest:request error:&error] lastObject];
if (error) {
NSLog(#"Error getting the country from core data: %#", error);
}
country.downloaded = 1;
error = nil;
if (![self.managedObjectContext save:&error]) {
NSLog(#"Saving changes to country failed: %#", error);
}
}
FetchedResultsController:
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
/*
Set up the fetched results controller.
*/
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Country" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Sort using the name property.
NSSortDescriptor *sortDownloaded = [[NSSortDescriptor alloc] initWithKey:#"downloaded" ascending:NO];
NSSortDescriptor *sortName = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
[fetchRequest setSortDescriptors:#[sortDownloaded, sortName]];
// Use the sectionIdentifier property to group into sections.
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"downloaded" cacheName:nil];
_fetchedResultsController.delegate = self;
self.fetchedResultsController = _fetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return _fetchedResultsController;
}
Initializing Restkit:
//Enable RestKit logging
//RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelInfo);
//RKLogConfigureByName("RestKit/CoreData", RKLogLevelTrace);
//RKLogConfigureByName("RestKit/Network", RKLogLevelTrace);
// Initialize RestKit
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:BaseURLString]];
// Initialize managed object store
NSError *error = nil;
NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"FGDataModel" ofType:#"momd"]];
NSManagedObjectModel *managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
BOOL success = RKEnsureDirectoryExistsAtPath(RKApplicationDataDirectory(), &error);
if (!success) {
RKLogError(#"Failed to create Application Data Directory at path '%#': %#", RKApplicationDataDirectory(), error);
}
objectManager.managedObjectStore = managedObjectStore;
NSString *path = [RKApplicationDataDirectory() stringByAppendingPathComponent:#"FGDataModel.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);
}
// Configure a managed object cache to ensure we do not create duplicate objects
managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
[managedObjectStore createManagedObjectContexts];
// Set the default store shared instance
[RKManagedObjectStore setDefaultStore:managedObjectStore];
// Setup our object mappings
[FGDataCalls setupObjectMappings:managedObjectStore withObjectManager:objectManager];
// Save the MOC
AppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
appDelegate.managedObjectContext = managedObjectStore.mainQueueManagedObjectContext;
I've tried saving the context as many different ways at this point, including blocks, and I haven't yet had the fetchedResultsController recognize the update and even call controllerWillChangeContent: or controllerDidChangeContent:. I have successfully deleted a row using the MOC's deleteObject: method, so I have the context in the correct state. I can even get the object from my fetchedResultsController after the downloaded attribute has been set and see that it is correct, but the table never updates. Additionally, forcing the table to reload after I have changed the attribute doesn't yield any result. I know this is a specific case, but hopefully someone else has run into the same issue or can see where I'm going wrong.
Thanks!
When saving you shouldn't do
error = nil;
if (![self.managedObjectContext save:&error]) {
NSLog(#"Saving changes to country failed: %#", error);
}
you should do
error = nil;
if (![self.managedObjectContext saveToPersistentStore:&error]) {
NSLog(#"Saving changes to country failed: %#", error);
}
That said, assuming the FRC is connected to the same context then it should see changes before they're saved. You also don't show your FRC delegate methods so there could be an issue there.
I'm having a bit of a problem with my code, I'm actually using RestKit to map various objects in core data, that works well, then I need to edit certain object, when I edit it and save the context all works very well and without problem, the trouble is when I stop de application and open it again, none of the changes I've made are saved, here is my code:
[managedObjectStore createPersistentStoreCoordinator];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:#"TaskModel.sqlite"];
NSString *seedPath = [[NSBundle mainBundle] pathForResource:#"RKSeedDatabase" 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];
then this is my update method:
// Get data
// 1.- Create the request object:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSManagedObjectContext *localContext = [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext;
// 3.- Define the type of managed object you need:
NSEntityDescription *entity = [NSEntityDescription entityForName:#"SavedTask" inManagedObjectContext:localContext];
[fetchRequest setEntity:entity];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSPredicate *searchTasktoComplete = [NSPredicate predicateWithFormat:#"taskId == %#", self.task.taskId];
[fetchRequest setPredicate:searchTasktoComplete];
// 5.- Execute the request:
NSError *error;
NSArray *fetchedTasks = [localContext executeFetchRequest:fetchRequest error:&error];
SavedTask *taskToComplete = [fetchedTasks objectAtIndex:0];
//complete the task in core data
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"MM/dd/yyyy"];
NSString *date = [dateFormat stringFromDate:[NSDate date]];
[taskToComplete setRealDate:date];
[taskToComplete setFollowUp:#"100"];
[localContext refreshObject:taskToComplete mergeChanges:YES];
[localContext save:&error];
if (![localContext save:&error]) {
NSLog(#"error");
}
can anyone help me?, thanks in advance
You should use the saveToPersistentStore method
NSError *error = nil;
if(![localContext saveToPersistentStore:&error]){
NSLog(#"Failed to save to data store");
}
Try this!
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
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.
My app is setup so that when it's first used, it downloads the required data from a web based xml feed.
The user also has the option to periodically refresh the data via a setting.
When they do this, I want to delete the existing database and then recreate it via the code I use for the first load.
I read that simply deleting the database is not the correct way to do this so I'm using the following to destroy the data before loading the new dataset.
- (void)resetApplicationModel {
NSURL *_storeURL = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: DBSTORE]];
NSPersistentStore *_store = [persistentStoreCoordinator persistentStoreForURL:_storeURL];
[persistentStoreCoordinator removePersistentStore:_store error:nil];
[[NSFileManager defaultManager] removeItemAtPath:_storeURL.path error:nil];
[persistentStoreCoordinator release], persistentStoreCoordinator = nil;
}
However this doesn't work, when performing a data refresh, it downloads the data but can't save it to the database and generates the following error in the console;
This NSPersistentStoreCoordinator has no persistent stores. It cannot perform a save operation.
What's the "correct" way to refresh a database?
The "right" way to do this is to just fetch all of the objects, delete each of them, and then save the context. (http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdFetching.html)
- (void) deleteAllEntitiesForName:(NSString*)entityName {
NSManagedObjectContext *moc = [self managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription
entityForName:entityName inManagedObjectContext:moc];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];
NSError *error = nil;
NSArray *array = [moc executeFetchRequest:request error:&error];
if (array != nil) {
for(NSManagedObject *managedObject in array) {
[moc deleteObject:managedObject];
}
error = nil;
[moc save:&error];
}
}
Then you can just recreate your objects.