I have deleted all of the entries stored in Core Data at the time of logout in my FBChat application by using the methods as follows.
//delete persistance.......
if ([__persistentStoreCoordinator persistentStores] == nil)
return;
[self.managedObjectContext reset];
[self.managedObjectContext lock];
NSPersistentStore *store = [[self.persistentStoreCoordinator persistentStores] lastObject];
if (![self.persistentStoreCoordinator removePersistentStore:store error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
// Delete file
if ([[NSFileManager defaultManager] fileExistsAtPath:store.URL.path]) {
if (![[NSFileManager defaultManager] removeItemAtPath:store.URL.path error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
__persistentStoreCoordinator = nil;
__persistentStoreCoordinator = [self persistentStoreCoordinator];
[self.managedObjectContext unlock];
But When I am log in again, fetchcontroller not getting the values. I have set the fetchcontroller=nil while logout. The delegate methods for the fetchcontroller are getting called at the login time.
If anybody having idea then please help me. Thanks in advance
This is expected behavior. After deleting everything, you are not getting values. If your fetched results controller is created lazily (code not shown, but that is usually the design pattern), it will create itself when needed.
No surprises here.
Related
I am trying to get NSPersistentStoreCoordinator to manage the deletion and insertion of multiple persistent stores. So far I have managed to configure the PSC with two stores and I have been able to remove either store by specifying its index.
Like this…
NSPersistentStore *store = [[self.persistentStoreCoordinator persistentStores] objectAtIndex:0];
if (![self.persistentStoreCoordinator removePersistentStore:store error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
[[NSFileManager defaultManager] removeItemAtURL:store.URL error:&error];
But I'm finding that when I add the store back in to the PSC the index value is incorrect and it cannot be specified with the existing class methods. The consequence of this is that the new data is downloaded and added to the wrong store.
Does anyone have any suggestions on how to this should be done?
Background (Updated)
The reason for using two persistent stores is so that I can designate a unique store for the two xml documents that will be downloaded over the network. As both these files are relatively large I am hoping to reduce network traffic. So a check is done to see if either file has been modified. If they have then the corresponding persistent store is deleted and a new store added. It's at this point the problem starts. Adding a new store always adds it to the end of the Persistent Stores Array. This appears to create a mismatch in the stores when merging the data back with the MOC.
Code
Here's what I've tried so far which removes and adds the persistent store but the new data is added to the wrong store.
static NSString * const kLastDateStoreUpdateKey = #"eventLastStoreUpdateKey";
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSString *last_modified = [NSString stringWithFormat:#"%#",[[(NSHTTPURLResponse *)response allHeaderFields] objectForKey:#"Last-Modified"]];
NSDateFormatter *dFormatter = [[NSDateFormatter alloc] init];
[dFormatter setDateFormat:#"EEE, dd MMM yyyy HH:mm:ss zzz"];
[dFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"en_GB"]];
[dFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"GMT"]];
dateModified = [dFormatter dateFromString:last_modified];
NSDate *previousDate = [[NSUserDefaults standardUserDefaults] objectForKey:kLastDateStoreUpdateKey];
if (!previousDate || [previousDate compare:dateModified] != NSOrderedSame) {
[self.managedObjectContext lock];
[self.managedObjectContext reset];
if ([[NSFileManager defaultManager] fileExistsAtPath:self.persistentStorePath]) {
NSError *error = nil;
NSArray *stores = [self.persistentStoreCoordinator persistentStores];
NSURL *storeUrls = [NSURL fileURLWithPath:persistentStorePath];
for (NSPersistentStore *store in stores){
if ([[store.URL absoluteURL] isEqual:[storeUrls absoluteURL]]) {
if (![self.persistentStoreCoordinator removePersistentStore:store error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
[[NSFileManager defaultManager] removeItemAtURL:store.URL error:&error];
NSLog(#"Check store removed %#", [self.persistentStoreCoordinator persistentStores]);
}
}
}
NSURL *storeUrl = [NSURL fileURLWithPath:persistentStorePath];
NSError *error = nil;
[self.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error];
if (error) {
NSLog(#"event error %# %#",error, [[self.persistentStoreCoordinator persistentStores] objectAtIndex:0]);
}
NSLog(#"Check store added %#",self.persistentStoreCoordinator.persistentStores);
[self.managedObjectContext unlock];
}else {
[self cancelDownload];
NSLog(#"event cancel %# %# %#",previousDate, dateModified, [self.persistentStoreCoordinator persistentStores]);
}
}
Solved
As Hunter pointed out below my PSC Configuration was set to as nil. So data was not being added to each individual Persistent Store. When I created a Configuration in the Core Data Model and targeted the relevant Entities I set the PSC to have that configuration. It then worked as expected. See below.
NSURL *storeUrl = [NSURL fileURLWithPath:persistentStorePath];
NSError *error = nil;
[self.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:#"ConfigEvent" URL:storeUrl options:nil error:&error];
if (error) {
NSLog(#"event error %# %#",error, [[self.persistentStoreCoordinator persistentStores] objectAtIndex:0]);
}
NSLog(#"Check store added %#",self.persistentStoreCoordinator.persistentStores);
If you truly require the two stores [I can't tell from your explanation if that's really the best way to do this], you might want to reference them via properties or an ivar instead of relying on the array.
Alternatively, you could iterate through the PSC's stores array and identify each one by inspecting the URL.
Update:
If you're having trouble storing specific data in different NSPersistentStores, take a look at Core Data configurations. This allows you to tell Core Data to put specific entities in specific persistent stores.
You specify configurations in your model and when you add persistent stores. More here: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdMOM.html
You should be able to achieve this with one Persistent Store Coordinator and two Managed Object Contexts. You only need to go to the complexity of two Persistent Store Coordinators if you are experiencing locking which is hurting performance.
Remember: The Simplest Possible Thing (tm)
Could anyone see any possible reasons why?
Friend *newFriend = [NSEntityDescription insertNewObjectForEntityForName:#"Friend" inManagedObjectContext:managedObjectContext];
newFriend.name = #"Jim";
newFriend.age = [NSNumber numberWithInt:5];
NSError *error = nil;
if ([managedObjectContext save:&error])
{
NSLog(#"error %#", error);
}
managedObjectContext was passed to the view controller where this code is from the application delegate.
Thanks
if (![managedObjectContext save:&error])
{
NSLog(#"error %#", error);
}
that should be
You should expect error to continue to be nil if save: succeeded (and therefore returned YES as you're testing). What behavior did you expect here?
I am trying to do a migration
I have 2 versions of model
1.xcdatamodel
2.xcdatamodel
I created a mapping model from version 1 to 2
1to2.xcmappingmodel
The problem is that it can't find the migration model that I created so mappingModel always gets nil.
Is there anything I have to do to specify what mappingModel it ahould use?
target = [[NSManagedObjectModel alloc] initWithContentsOfURL:[NSURL fileURLWithPath:modelPath]];
//target and source are initialized correctly
mappingModel = [NSMappingModel mappingModelFromBundles:nil forSourceModel:source destinationModel:target];
It might be that you changed one of your models after creating the mapping model.
Even if a change does not seem relevant it will change the hash value of the model which is used for finding the appropriate mapping model.
At least I've been bitten by this just now :-)
If you've already created a mapping model from 1.xcdatamodel to 2.xcdatamodel, and properly configured it, then you should be able to do something like this: [Note: the key is specifying NSMigratePersistentStoresAutomaticallyOption]
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (persistentStoreCoordinator)
return persistentStoreCoordinator;
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"MyStore.sqlite"]];
NSError *error = nil;
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, nil];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeUrl
options:options
error:&error])
{
// Handle error
NSLog(#"Error adding persistent store...%#", error);
// Handle the error.
NSLog(#"Failed to save to data store: %#", [error localizedDescription]);
NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
if(detailedErrors != nil && [detailedErrors count] > 0)
{
for(NSError* detailedError in detailedErrors)
{
NSLog(#" DetailedError: %#", [detailedError userInfo]);
}
}
else
{
NSLog(#" %#", [error userInfo]);
}
}
else
{
DLog(#"Persistent store added without incident, apparently.");
}
return persistentStoreCoordinator;
}
To answer the original question, your code looks alright but I'm not why you passed nil as the bundles parameter. The documentation doesn't say one can. So:
NSArray *theBundles = [NSArray arrayWithObject:[NSBundle mainBundle]];
mappingModel = [NSMappingModel mappingModelFromBundles:theBundles
forSourceModel:source
destinationModel:target];
If you pass nil as bundle parameter, it will take [NSBundle mainBundle].
[To response to question of Elise van Looij]
I have started using CoreData in my project. Without the pieces of code from CodeData my project was working pretty fine. I added the methods for accessing the NSManagedObjectContext from the coreData project template. Now I try to create new CoreData object with the following code:
- (void)saveSearchResultToHistory:(NSArray *) productsArray {
[productsArray retain];
NSLog(#"context: %#", self.managedObjectContext);
Product *product = [NSEntityDescription
insertNewObjectForEntityForName:#"Product"
inManagedObjectContext:self.managedObjectContext];
product.productId = [(Product *) [productsArray objectAtIndex:0] productId];
NSError *error;
if (![self.managedObjectContext save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
[productsArray release];
}
When this method is ran once then everything is fine, when I try to run it for the second time, the processing is stopped at:
Product *product = [NSEntityDescription
insertNewObjectForEntityForName:#"Product"
inManagedObjectContext:self.managedObjectContext];
with the following error message in the console:
[CFString retain]: message sent to deallocated instance 0x5a23b0
Any ideas what might be wrong?
Thanks!
First off you do not need to save the context every time you add something, just save when the app closes or goes in the background.
The error you are getting looks like you over release a NSString some where.
To check if the error isn't in the coredata context use this save function:
- (void)saveContext {
if ([self.managedObjectContext hasChanges]) {
NSError *error = nil;
if (![self.managedObjectContext save:&error]) {
dbgPrint(#"Failed to save to data store: %#", [error localizedDescription]);
NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
if (detailedErrors != nil && [detailedErrors count] > 0) {
for(NSError* detailedError in detailedErrors) {
dbgPrint(#"--DetailedError: %#", [detailedError userInfo]);
}
} else {
dbgPrint(#" %#", [error userInfo]);
}
}
}
}
I want to reset the data store clean by removing the app's sqlite file. I wrote this function in my data helper class:
-(void) resetPersistenStore {
NSError *error = nil;
[persistentStoreCoordinator_ release];
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"MyApp.sqlite"];
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:&error];
if (error) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
persistentStoreCoordinator_ = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![persistentStoreCoordinator_ addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
managedObjectModel_ = nil;
}
I put this following test in UIApplication::didFinishLaunchingWithOptions
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
/* test */
[TestDataHelper populateTestData:[self managedObjectContext]];
[TestDataHelper populateTestData:[self managedObjectContext]];
[self resetPersistenStore];
[TestDataHelper populateTestData:[self managedObjectContext]];
[TestDataHelper testPopulateTestData:[self managedObjectContext]];
Instead of one set of data created by the function populateTestData, I can see actually three set of data (because I called the function three times)
It is clear that resetPersistenStore() works, because without it, the data will keep accumulating.
So my question is:
Why the reset does not take effect immediately?
I have set managedObjectContext to nil in the reset function, but it did not help.
Here is my populateTestData function
+(void)populateTestData:(NSManagedObjectContext*)managedObjectContext {
Stock* s1 = (Stock*)[NSEntityDescription insertNewObjectForEntityForName:#"Stock"
inManagedObjectContext:managedObjectContext];
s1.code = #"ABC2";
s1.name = #"ABC";
s1.enteredBy = #"akong";
[managedObjectContext save:&error];
if (error) {
NSLog(#"Data error %#", [error description]);
} else {
NSLog(#"Init completed");
}
}
Even though the context saved the data in populateTestData, it still has data loaded in it.
Suppose when the application starts, the file has 3 objects. Now the first populateTestData message adds an object into the context, and save it. So 4 objects in the file, 1 in the context. After the second message, 5 in the file, 2 in the context. Now the file is gone, but still there are 2 in the context. Now the final message adds another object to the context and to the file, so there are 3 objects.
You said setting managedObjectContext to nil in the reset function didn't help. I doubt if the context has been properly reset there. Try [managedObjectContext reset] instead.
I am only speculating here but are you by any chance using a fetchedResultsController to verify/display the results of these operations? If so try to set its caching to nil and see if it helps.
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"xxxxxxxx" cacheName:nil];
Cheers,
Rog
It may simply be an artifact of the simulator. If you're running in the simulator, you may need to reset it. From the iOS Simulator menu, select Reset Content and Settings...