context not calling performblock - iphone

http://www.fileconvoy.com/dfl.php?id=g021ae486d8d8acb3999343609097fcba7f7befa53
this is the link to an assignment app which i got for my homework it most of its concepts taken from the stanford cs193p course as iam studing that too, this app has 3 modules
1 camera
2 map
3 contacts
the camera and map modules are working fine not as hoped but fine cut in the contacts module I
am using core data model to store contacts from contacts directory which you'll see when you run the app.
Now the problem is that my view adds the contacts to the core data but they are not showing in the table so when i tried to debug the application i saw that the method
[self.managedObjectContext performBlockAndWait:^{}];
is not calling the block of code inside it thats why the data is not even getting saved in the data model let alone show in table.
very very thanks in advance...
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
url = [url URLByAppendingPathComponent:#"Demo Document"];
UIManagedDocument *document = [[UIManagedDocument alloc] initWithFileURL:url];
if (![[NSFileManager defaultManager] fileExistsAtPath:[url path]]) {
[document saveToURL:url
forSaveOperation:UIDocumentSaveForCreating
completionHandler:^(BOOL success) {
if (success) {
self.managedObjectContext = document.managedObjectContext;
}
}];
} else if (document.documentState == UIDocumentStateClosed) {
[document openWithCompletionHandler:^(BOOL success) {
if (success) {
self.managedObjectContext = document.managedObjectContext;
}
}];
} else {
self.managedObjectContext = document.managedObjectContext;
}
if(self.managedObjectContext==nil)
NSLog(#"nil object");

Related

Application is crashing when creating a options Dictionary used by addPersistentStoreWithType method

I am using encrypted-core-data to encrypts all data that is persisted , previously simple CoreData was using. persistentStoreCoordinator creation code are as follows .
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
NSURL *oldStoreURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"VistaJetApp.sqlite"];
NSURL *newStoreURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"VistaJet.sqlite"];
NSError *error = nil;
NSString *currentPassword = [[VJAesCryptoWrapper getInstance] getCurrentPassword];
NSDictionary *options = [self getEncryptedStoreOptionsWithPassword:currentPassword andDatabaseStore:newStoreURL];
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
//if old store not exists , it means fresh installation
if([[NSFileManager defaultManager] fileExistsAtPath:oldStoreURL.path] == NO) {
if (![_persistentStoreCoordinator addPersistentStoreWithType:EncryptedStoreType configuration:nil URL:newStoreURL options:options error: &error]) {
}
} else {
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:oldStoreURL options:#{NSMigratePersistentStoresAutomaticallyOption:#YES, NSInferMappingModelAutomaticallyOption:#YES} error: &error]) {
}
NSPersistentStore *oldUnsecureStore = [_persistentStoreCoordinator persistentStoreForURL:oldStoreURL];
[ConsoleLogger logText:[NSString stringWithFormat:#"Migration started"]];
//start migration
if(![_persistentStoreCoordinator migratePersistentStore:oldUnsecureStore toURL:newStoreURL options:options withType:EncryptedStoreType error:&error]) {
} else {
[[NSFileManager defaultManager] removeItemAtURL:oldStoreURL error:nil];
}
}
return _persistentStoreCoordinator;
}
Creation of options dictionary
- (NSDictionary*)getEncryptedStoreOptionsWithPassword:(NSString*)password andDatabaseStore:(NSURL*)storeUrl {
return #{ EncryptedStorePassphraseKey: password,
EncryptedStoreDatabaseLocation: storeUrl,
NSMigratePersistentStoresAutomaticallyOption:#YES,
NSInferMappingModelAutomaticallyOption:#YES
};
}
I am saving password in keychain using KeychainItemWrapper and my code crashing exactly on getEncryptedStoreOptionsWithPassword:currentPassword method. App is live and I am not able to reproduce the crash , but on crashlytics it is showing so many crashes
crashlytics crash logs image
Also using AESCrypt to encrypt password then saving it to keychain using KeychainItemWrapper.
Observation:
The crash that crashlytics is displaying only appear when we upload build on test flight using distribution profile.
crash is happening 100% on iOS 11 as reported by crashlytics
I think this is a known bug in iOS 10 that you are seeing, there is a work-around: enable "Keychain Sharing" (under your app -> Capabilities tab in Xcode).
KeychainItemWrapper crash on iOS10

Core Data - UIManagedDocument won't open

I am using Core Data to store a few simple strings related to each user. When the app first starts, everything seems to be fine. The database opens, and I am successfully able to save and retrieve data.
However, after some usage, sometimes the UIManagedDocument I use will just not open when the app starts. Here is the method I use for that (done in the app delegate):
-(void)initManagedDocument{
#try {
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
url = [url URLByAppendingPathComponent:#"DataBase"];
self.managedDocument = [[UIManagedDocument alloc] initWithFileURL:url];
if ([[NSFileManager defaultManager] fileExistsAtPath:[url path]]){
[self.managedDocument openWithCompletionHandler:^(BOOL success){
if (success) {
[self documentIsReady];
}else{
NSLog(#"Could not open document");
}
}];
}else{
[self.managedDocument saveToURL:url forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success){
if (success) {
[self documentIsReady];
}else{
NSLog(#"Could not create document");
}
}];
}
}
#catch (NSException *e) {
}
}
This code gets called from my app's didFinishLaunchingWithOptions. The saveToURL half of the if-statement gets called initially, and returns a success. Then in the following few calls the openWithCompletionHandler: gets called, and returns successfully.
However, at some point after using the app for awhile, the openWithCompletionHandler: returns success = FALSE. I am not sure why, or how the UIManagedDocument gets messed up. The URL still seems to be the same, and the fileExistsAtPath is still returning YES.
Does anyone know why this might be happening? Or if there is a way for me to debug and find out what the actual error is that is causing the open to fail?

Can't use core database in IOS5

I am working with a core database, it is working in IOS 6 but when I trying to test it on IOS 5. It does not do anything. Let me explain what I'm doing.
First I do this in my viewWillAppear.
- (void)viewWillAppear:(BOOL)animated
{
NSLog(#"view appeared");
[super viewWillAppear:animated];
if (!self.genkDatabase) {
NSLog(#"comes to here");
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
url = [url URLByAppendingPathComponent:#"Default appGenk Database2"];
// url is now "<Documents Directory>/Default Photo Database"
self.genkDatabase = [[UIManagedDocument alloc] initWithFileURL:url]; // setter will create this for us on disk
NSLog(#"database created on disk");
}
}
Then it comes in the UseDocument method.
- (void)useDocument
{
NSLog(#"Comses in the use document");
if (![[NSFileManager defaultManager] fileExistsAtPath:[self.genkDatabase.fileURL path]]) {
// does not exist on disk, so create it
[self.genkDatabase saveToURL:self.genkDatabase.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
NSLog(#"create");
[self setupFetchedResultsController];
[self fetchGenkDataIntoDocument:self.genkDatabase];
}];
} else if (self.genkDatabase.documentState == UIDocumentStateClosed) {
NSLog(#"closed news");
// exists on disk, but we need to open it
[self.genkDatabase openWithCompletionHandler:^(BOOL success) {
[self setupFetchedResultsController];
}];
} else if (self.genkDatabase.documentState == UIDocumentStateNormal) {
NSLog(#"normal");
// already open and ready to use
[self setupFetchedResultsController];
}
}
And finally it goes into the setDatabase Method.
- (void)setGenkDatabase:(UIManagedDocument *)genkDatabase
{
if (_genkDatabase != genkDatabase) {
_genkDatabase = genkDatabase;
[self useDocument];
}
NSLog(#"Comes in the setdatabase methode.");
}
Doing all this gives the following log.
2012-10-22 10:42:47.444 RacingGenk[4786:c07] view appeared
2012-10-22 10:42:47.445 RacingGenk[4786:c07] comes to here
2012-10-22 10:42:47.459 RacingGenk[4786:c07] Comses in the use document
2012-10-22 10:42:47.460 RacingGenk[4786:c07] Comes in the setdatabase methode.
2012-10-22 10:42:47.461 RacingGenk[4786:c07] database created on disk
Like you can see it does not print the create log in my use document. So it isn't able to execute the method FetchDataIntoDocument.
Can anybody help me with this problem. I am searching at this problem for ages for now.
Many thanks in advace.
Stef
Are you testing with the simulator or on the device?
The simulator is case-insensitive - the device is case-sensitive on file access, remember that!
But beside from that the documentation for NSFileManager recommends not checking to see if files exist, and instead just trying to read the file and handle any errors gracefully (e.g. file not found error). So just try loading the file instead of checking to see if it exists.
And i don't see any file extension for your database file! It just states ""Default appGenk Database2".
Is this already the file name or jut another subdirectory so far?
EDIT:
You can try the following code:
- (void) setupStore {
NSString* storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"mydatabase.sqlite"];
// Set up the store.
NSFileManager* fileManager = [NSFileManager defaultManager];
// If the expected store doesn't exist, create one.
if (![fileManager fileExistsAtPath: storePath]) {
// create your store here!
}
}
- (NSString*) applicationDocumentsDirectory {
return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}
"mydatabase.sqlite" is the name of the database-file you expect to be present in your documents directory.
If you would like to see a full-fledged example on how to set-up a core data persistent store you could check out apples own iPhoneCoreDataRecipes example. Just take a look at the
RecipesAppDelegate.m
implementation.

CoreData versioning and blocking lightweight migration

I have enabled versioning of my Core Data model and have been using light weight migration. My code always tries to do lightweight migration and then if that fails because the model are incompatible it falls back to deleting all existing data and refetching from the server.
So lightweight migration is just used for efficiency and isn't required for correctness.
What I want to do now is make a change to my model which in theory lightweight migration could handle but in fact I need new data from the server. I want to somehow flag the model and not upgradeable via lightweight migration. For example if a field name has not changed but the meaning of that field has changed in such a way that the old code is incompatible with the new code base. (This is just an example.)
Has anyone found a way to flag a two models as incompatible so lightweight migration won't upgrade them?
I've struggled with the same problem before.
I have a method that will attempt to migrate data using Mapping Models which is what you should use if you're going to turn off lightweight migration.
If you aren't going to do a lot of fancy data mapping, xcode will automatically create a mapping model that will work exactly like lightweight migration. All you have to do is create a new "Mapping Model" file each time you add a new version to Core Data. Just go to "File -> New -> New File" and under Core Data there should be a Mapping Model template. Select it and choose the source and destination versions.
I don't have my code openly available on github so I'll just post the migration method here.
- (BOOL)progressivelyMigrateURL:(NSURL*)sourceStoreURL ofType:(NSString*)type toModel:(NSManagedObjectModel*)finalModel
{
NSError *error = nil;
// if store dosen't exist skip migration
NSString *documentDir = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
if(![NSBundle pathForResource:#"YongoPal" ofType:#"sqlite" inDirectory:documentDir])
{
migrationProgress = 1.0;
[self performSelectorOnMainThread:#selector(updateMigrationProgress) withObject:nil waitUntilDone:YES];
// remove migration view
[self.migrationView performSelectorOnMainThread:#selector(setHidden:) withObject:[NSNumber numberWithBool:YES] waitUntilDone:YES];
[self.migrationView performSelectorOnMainThread:#selector(removeFromSuperview) withObject:nil waitUntilDone:YES];
self.migrationView = nil;
self.migrationProgressLabel = nil;
self.migrationProgressView = nil;
self.migrationSpinner = nil;
return YES;
}
//START:progressivelyMigrateURLHappyCheck
NSDictionary *sourceMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:type URL:sourceStoreURL error:&error];
if (!sourceMetadata)
{
return NO;
}
if ([finalModel isConfiguration:nil compatibleWithStoreMetadata:sourceMetadata])
{
migrationProgress = 1.0;
[self performSelectorOnMainThread:#selector(updateMigrationProgress) withObject:nil waitUntilDone:YES];
// remove migration view
[self.migrationView performSelectorOnMainThread:#selector(setHidden:) withObject:[NSNumber numberWithBool:YES] waitUntilDone:YES];
[self.migrationView performSelectorOnMainThread:#selector(removeFromSuperview) withObject:nil waitUntilDone:YES];
self.migrationView = nil;
self.migrationProgressLabel = nil;
self.migrationProgressView = nil;
self.migrationSpinner = nil;
error = nil;
return YES;
}
else
{
migrationProgress = 0.0;
[self.migrationView performSelectorOnMainThread:#selector(setHidden:) withObject:NO waitUntilDone:YES];
[self performSelectorOnMainThread:#selector(updateMigrationProgress) withObject:nil waitUntilDone:YES];
}
//END:progressivelyMigrateURLHappyCheck
//START:progressivelyMigrateURLFindModels
//Find the source model
NSManagedObjectModel *sourceModel = [NSManagedObjectModel mergedModelFromBundles:nil forStoreMetadata:sourceMetadata];
if(sourceModel == nil)
{
NSLog(#"%#", [NSString stringWithFormat:#"Failed to find source model\n%#", [sourceMetadata description]]);
return NO;
}
//Find all of the mom and momd files in the Resources directory
NSMutableArray *modelPaths = [NSMutableArray array];
NSArray *momdArray = [[NSBundle mainBundle] pathsForResourcesOfType:#"momd" inDirectory:nil];
for (NSString *momdPath in momdArray)
{
NSAutoreleasePool *pool = [NSAutoreleasePool new];
NSString *resourceSubpath = [momdPath lastPathComponent];
NSArray *array = [[NSBundle mainBundle] pathsForResourcesOfType:#"mom" inDirectory:resourceSubpath];
[modelPaths addObjectsFromArray:array];
[pool drain];
}
NSArray* otherModels = [[NSBundle mainBundle] pathsForResourcesOfType:#"mom" inDirectory:nil];
[modelPaths addObjectsFromArray:otherModels];
if (!modelPaths || ![modelPaths count])
{
//Throw an error if there are no models
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setValue:#"No models found in bundle" forKey:NSLocalizedDescriptionKey];
//Populate the error
error = [NSError errorWithDomain:#"com.yongopal.coredata" code:500 userInfo:dict];
if([[self.prefs valueForKey:#"debugMode"] isEqualToString:#"Y"])
{
NSLog(#"error: %#", error);
}
return NO;
}
//END:progressivelyMigrateURLFindModels
//See if we can find a matching destination model
//START:progressivelyMigrateURLFindMap
NSMappingModel *mappingModel = nil;
NSManagedObjectModel *targetModel = nil;
NSString *modelPath = nil;
for(modelPath in modelPaths)
{
targetModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:[NSURL fileURLWithPath:modelPath]];
mappingModel = [NSMappingModel mappingModelFromBundles:nil forSourceModel:sourceModel destinationModel:targetModel];
//If we found a mapping model then proceed
if(mappingModel)
{
break;
}
else
{
//Release the target model and keep looking
[targetModel release];
targetModel = nil;
}
}
//We have tested every model, if nil here we failed
if (!mappingModel)
{
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setValue:#"No mapping models found in bundle" forKey:NSLocalizedDescriptionKey];
error = [NSError errorWithDomain:#"com.yongopal.coredata" code:500 userInfo:dict];
if([[self.prefs valueForKey:#"debugMode"] isEqualToString:#"Y"])
{
NSLog(#"error: %#", error);
}
return NO;
}
//END:progressivelyMigrateURLFindMap
//We have a mapping model and a destination model. Time to migrate
//START:progressivelyMigrateURLMigrate
NSMigrationManager *manager = [[NSMigrationManager alloc] initWithSourceModel:sourceModel destinationModel:targetModel];
// reg KVO for migration progress
[manager addObserver:self forKeyPath:#"migrationProgress" options:NSKeyValueObservingOptionNew context:NULL];
NSString *modelName = [[modelPath lastPathComponent] stringByDeletingPathExtension];
NSString *storeExtension = [[sourceStoreURL path] pathExtension];
NSString *storePath = [[sourceStoreURL path] stringByDeletingPathExtension];
//Build a path to write the new store
storePath = [NSString stringWithFormat:#"%#.%#.%#", storePath, modelName, storeExtension];
NSURL *destinationStoreURL = [NSURL fileURLWithPath:storePath];
if (![manager migrateStoreFromURL:sourceStoreURL type:type options:nil withMappingModel:mappingModel toDestinationURL:destinationStoreURL destinationType:type destinationOptions:nil error:&error])
{
if([[self.prefs valueForKey:#"debugMode"] isEqualToString:#"Y"])
{
NSLog(#"error: %#", error);
}
[targetModel release];
[manager removeObserver:self forKeyPath:#"migrationProgress"];
[manager release];
return NO;
}
[targetModel release];
[manager removeObserver:self forKeyPath:#"migrationProgress"];
[manager release];
//END:progressivelyMigrateURLMigrate
//Migration was successful, move the files around to preserve the source
//START:progressivelyMigrateURLMoveAndRecurse
NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString];
guid = [guid stringByAppendingPathExtension:modelName];
guid = [guid stringByAppendingPathExtension:storeExtension];
NSString *appSupportPath = [storePath stringByDeletingLastPathComponent];
NSString *backupPath = [appSupportPath stringByAppendingPathComponent:guid];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager moveItemAtPath:[sourceStoreURL path] toPath:backupPath error:&error])
{
if([[self.prefs valueForKey:#"debugMode"] isEqualToString:#"Y"])
{
NSLog(#"error: %#", error);
}
//Failed to copy the file
return NO;
}
//Move the destination to the source path
if (![fileManager moveItemAtPath:storePath toPath:[sourceStoreURL path] error:&error])
{
if([[self.prefs valueForKey:#"debugMode"] isEqualToString:#"Y"])
{
NSLog(#"error: %#", error);
}
//Try to back out the source move first, no point in checking it for errors
[fileManager moveItemAtPath:backupPath toPath:[sourceStoreURL path] error:nil];
return NO;
}
//We may not be at the "current" model yet, so recurse
return [self progressivelyMigrateURL:sourceStoreURL ofType:type toModel:finalModel];
//END:progressivelyMigrateURLMoveAndRecurse
}
This is an edited version of a method I got from some Core Data book I can't remember the title of. I wish I could give credit to the author. :S
Beware, I have some code in here that you should remove in your implementation. It's mostly stuff I use to update the view on the progress of the migration.
You can use this method like so:
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"YongoPal.sqlite"];
// perform core data migrations if necessary
if(![self progressivelyMigrateURL:storeURL ofType:NSSQLiteStoreType toModel:self.managedObjectModel])
{
// reset the persistent store on fail
NSString *documentDir = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
NSError *error = nil;
[[NSFileManager defaultManager] removeItemAtPath:[NSBundle pathForResource:#"YongoPal" ofType:#"sqlite" inDirectory:documentDir] error:&error];
}
else
{
NSLog(#"migration succeeded!");
}
Remember to remove the lightweight migration option before you use this.
Apple's Core Data Model Versioning and Data Migration Programming Guide addresses what to do if "you have two versions of a model that Core Data would normally treat as equivalent that you want to be recognized as being different", which I think is what you're asking.
The answer is to set the versionHashModifier on one of your Entities or Properties in the new model.
In your example, you'd do that on the field whose meaning has changed.

iOS 5 Core Data freeze

I try to do the following simple thing:
NSArray * entities = [context executeFetchRequest:inFetchRequest error:&fetchError];
Nothing fancy. But this freezes in iOS 5, it works fine in iOS 4. I don't get exceptions, warnings or errors; my app just simply freezes.
Please help me out! I'm dying here! ;)
I don't know if you also use different Thread. If yes the issue comes from the fact that NSManagedObjects themselves are not thread-safe. Creating a ManagedContext on the main thread and using it on another thread freezes the thread.
Maybe this article can help you :
http://www.cimgf.com/2011/05/04/core-data-and-threads-without-the-headache/
Apple has a demo application for handling Coredata on several threads (usually main & background threads) : http://developer.apple.com/library/ios/#samplecode/TopSongs/Introduction/Intro.html
What I've done to solve this issue is :
In the application delegate : create the persistent store (one for all thread) and create the Coredata managed Context for the main thread,
In the background thread, create a new managed context (from same persistent store)
Notifications are used when saving, to let the mainContext know when background thread has finished (inserting rows or other).
There are several solutions, using a NSQueueOperation. For my case, I'm working with a while loop. Here is my code if it may help you. However, Apple documentation on concurrency and their Top Songs example application are good points to start.
in the application delegate :
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
self.cdw = [[CoreDataWrapper alloc] initWithPersistentStoreCoordinator:[self persistentStoreCoordinator] andDelegate:self];
remoteSync = [RemoteSync sharedInstance];
...
[self.window addSubview:navCtrl.view];
[viewController release];
[self.window makeKeyAndVisible];
return YES;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator == nil) {
NSURL *storeUrl = [NSURL fileURLWithPath:self.persistentStorePath];
NSLog(#"Core Data store path = \"%#\"", [storeUrl path]);
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[NSManagedObjectModel mergedModelFromBundles:nil]];
NSError *error = nil;
NSPersistentStore *persistentStore = [persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error];
NSAssert3(persistentStore != nil, #"Unhandled error adding persistent store in %s at line %d: %#", __FUNCTION__, __LINE__, [error localizedDescription]);
}
return persistentStoreCoordinator;
}
-(NSManagedObjectContext *)managedObjectContext {
if (managedObjectContext == nil) {
managedObjectContext = [[NSManagedObjectContext alloc] init];
[managedObjectContext setPersistentStoreCoordinator:self.persistentStoreCoordinator];
}
return managedObjectContext;
}
-(NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator == nil) {
NSURL *storeUrl = [NSURL fileURLWithPath:self.persistentStorePath];
NSLog(#"Core Data store path = \"%#\"", [storeUrl path]);
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[NSManagedObjectModel mergedModelFromBundles:nil]];
NSError *error = nil;
NSPersistentStore *persistentStore = [persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error];
NSAssert3(persistentStore != nil, #"Unhandled error adding persistent store in %s at line %d: %#", __FUNCTION__, __LINE__, [error localizedDescription]);
}
return persistentStoreCoordinator;
}
-(NSManagedObjectContext *)managedObjectContext {
if (managedObjectContext == nil) {
managedObjectContext = [[NSManagedObjectContext alloc] init];
[managedObjectContext setPersistentStoreCoordinator:self.persistentStoreCoordinator];
}
return managedObjectContext;
}
-(NSString *)persistentStorePath {
if (persistentStorePath == nil) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths lastObject];
persistentStorePath = [[documentsDirectory stringByAppendingPathComponent:#"mgobase.sqlite"] retain];
}
return persistentStorePath;
}
-(void)importerDidSave:(NSNotification *)saveNotification {
if ([NSThread isMainThread]) {
[self.managedObjectContext mergeChangesFromContextDidSaveNotification:saveNotification];
} else {
[self performSelectorOnMainThread:#selector(importerDidSave:) withObject:saveNotification waitUntilDone:NO];
}
}
In the object running the background thread :
monitor = [[NSThread alloc] initWithTarget:self selector:#selector(keepMonitoring) object:nil];
-(void)keepMonitoring{
while(![[NSThread currentThread] isCancelled]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
AppDelegate * appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
//creating the cdw here will create also a new managedContext on this particular thread
cdwBackground = [[CoreDataWrapper alloc] initWithPersistentStoreCoordinator:appDelegate.persistentStoreCoordinator andDelegate:appDelegate];
...
}
}
Hope this help,
M.
Thanks for the hints given in this page on how to solve this freezing issue which appeared on upgrading from iOS4. It has been the most annoying problem I have found since I started programming on iOS.
I have found a quick solution for cases where there are just a few calls to the context from other threads.
I just use performSelectorOnMainThread:
[self performSelectorOnMainThread:#selector(stateChangeOnMainThread:) withObject: [NSDictionary dictionaryWithObjectsAndKeys:state, #"state", nil] waitUntilDone:YES];
To detect the places where the context is called from another thread you can put a breakpoint on the NSLog on the functions where you call the context as in the following piece of code and just use performSelectorOnMainThread on them.
if(![NSThread isMainThread]){
NSLog(#"Not the main thread...");
}
I hope that this may be helpful...
I had the same issue. If you run under the debugger and when the app "hangs" stop th app (use the "pause" button on the debugger. If you're at the executeFetchRequest line, then check the context variable. If it has a ivar _objectStoreLockCount and its greater than 1, then its waiting on a lock on the associated store.
Somewhere you're creating a race condition on your associated store.
This really sounds like trying to access a NSManagedObjectContext from a thread/queue other than the one that created it. As others suggested you need to look at your threading and make sure you are following Core Data's rules.
Executing fetch request must happen from the thread where context was created.
Remember it is not thread safe and trying to executeFetchRequest from another thread will cause unpredictable behavior.
In order to do this correctly, use
[context performBlock: ^{
NSArray * entities = [context executeFetchRequest:inFetchRequest error:&fetchError];
}];
This will executeFetchRequest in the same thread as context, which may or may not be the main thread.
In my case the app would freeze before 'executeFetchRequest' without any warning. The solution was to wrap all db operations in #synchronized(persistentStore).
Eg:
NSArray *objects;
#synchronized([self persistentStoreCoordinator]) {
objects = [moc executeFetchRequest:request error:&error];
}
Delete all object with fetchrequest doesn't work for me, the sqlite looks corrupted. the only way I found is
//Erase the persistent store from coordinator and also file manager.
NSPersistentStore *store = [self.persistentStoreCoordinator.persistentStores lastObject];
NSError *error = nil;
NSURL *storeURL = store.URL;
[self.persistentStoreCoordinator removePersistentStore:store error:&error];
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:&error];
//Make new persistent store for future saves (Taken From Above Answer)
if (![self.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
// do something with the error
}