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?
Related
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");
I am using CMIS(Content management interoperability services) to download data from the alfresco server. I am using the following code and it works fine to some extent but when the application goes to background, the network connection is lost and when the app comes to foreground, it tries to retry the download and fails saying connection error. As I am a newbie any help will be much appreciated.
- (void)testFileDownload
{
[self runTest:^
{
[self.session retrieveObjectByPath:#"/ios-test" completionBlock:^(CMISObject *object, NSError *error) {
CMISFolder *testFolder = (CMISFolder *)object;
STAssertNil(error, #"Error while retrieving folder: %#", [error description]);
STAssertNotNil(testFolder, #"folder object should not be nil");
CMISOperationContext *operationContext = [CMISOperationContext defaultOperationContext];
operationContext.maxItemsPerPage = 100;
[testFolder retrieveChildrenWithOperationContext:operationContext completionBlock:^(CMISPagedResult *childrenResult, NSError *error) {
STAssertNil(error, #"Got error while retrieving children: %#", [error description]);
STAssertNotNil(childrenResult, #"childrenCollection should not be nil");
NSArray *children = childrenResult.resultArray;
STAssertNotNil(children, #"children should not be nil");
STAssertTrue([children count] >= 3, #"There should be at least 3 children");
CMISDocument *randomDoc = nil;
for (CMISObject *object in children)
{
if ([object class] == [CMISDocument class])
{
randomDoc = (CMISDocument *)object;
}
}
STAssertNotNil(randomDoc, #"Can only continue test if test folder contains at least one document");
NSLog(#"Fetching content stream for document %#", randomDoc.name);
// Writing content of CMIS document to local file
NSString *filePath = [NSString stringWithFormat:#"%#/testfile", NSTemporaryDirectory()];
// NSString *filePath = #"testfile";
[randomDoc downloadContentToFile:filePath
completionBlock:^(NSError *error) {
if (error == nil) {
// Assert File exists and check file length
STAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:filePath], #"File does not exist");
NSError *fileError = nil;
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:&fileError];
STAssertNil(fileError, #"Could not verify attributes of file %#: %#", filePath, [fileError description]);
STAssertTrue([fileAttributes fileSize] > 10, #"Expected a file of at least 10 bytes, but found one of %d bytes", [fileAttributes fileSize]);
// Nice boys clean up after themselves
[[NSFileManager defaultManager] removeItemAtPath:filePath error:&fileError];
STAssertNil(fileError, #"Could not remove file %#: %#", filePath, [fileError description]);
} else {
STAssertNil(error, #"Error while writing content: %#", [error description]);
}
self.testCompleted = YES;
} progressBlock:nil];
}];
}];
}];
}
The connection fail doesn't occurs when the user presses the home key. It fails only when the magnetic cover lid is closed or when there is a timeout.
When an app is moved to background, the OS gives the app 5s to finish what it is doing before it is suspended (keeps RAM, but stops the app receiving any messages or doing anything). If you have a task that needs to run to completion when the user presses the home button, you can use a background task. From apple's documentation:
Your app delegate’s applicationDidEnterBackground: method has
approximately 5 seconds to finish any tasks and return. In practice,
this method should return as quickly as possible. If the method does
not return before time runs out, your app is killed and purged from
memory. If you still need more time to perform tasks, call the
beginBackgroundTaskWithExpirationHandler: method to request background
execution time and then start any long-running tasks in a secondary
thread. Regardless of whether you start any background tasks, the
applicationDidEnterBackground: method must still exit within 5
seconds.
Note: The UIApplicationDidEnterBackgroundNotification notification is
also sent to let interested parts of your app know that it is entering
the background. Objects in your app can use the default notification
center to register for this notification.
From http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html
USE Reachability code Try this code to save data once downloaded:
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
//try to access that local file for writing to it...
NSFileHandle *hFile = [NSFileHandle fileHandleForWritingAtPath:self.localPath];
//did we succeed in opening the existing file?
if (!hFile)
{ //nope->create that file!
[[NSFileManager defaultManager] createFileAtPath:self.localPath contents:nil attributes:nil];
//try to open it again...
hFile = [NSFileHandle fileHandleForWritingAtPath:self.localPath];
}
//did we finally get an accessable file?
if (!hFile)
{ //nope->bomb out!
NSLog("could not write to file %#", self.localPath);
return;
}
//we never know - hence we better catch possible exceptions!
#try
{
//seek to the end of the file
[hFile seekToEndOfFile];
//finally write our data to it
[hFile writeData:data];
}
#catch (NSException * e)
{
NSLog("exception when writing to file %#", self.localPath);
result = NO;
}
[hFile closeFile];
}
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.
I am using Core Data for the first time and was just curious if what I am seeing is correct. Each time I run the application via Xcode it reports that the database, exists, is closed, and is being opened. The next time I run the app the same happens ...
My question is, I am not closing the database myself and I was just curious if I have something wrong somewhere or if iOS is closing the database itself.
EDIT_001: Code Added.
- (void)viewDidLoad {
[super viewDidLoad];
if([self planetDatabase] == nil) {
// CREATE MANAGED DOCUMENT
NSLog(#"Database: Setup");
NSArray *userDocumentPath = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
NSURL *databaseFileURL = [[userDocumentPath lastObject] URLByAppendingPathComponent:#"DefaultPlanetDatabase"];
[self setPlanetDatabase:[[UIManagedDocument alloc] initWithFileURL:databaseFileURL]];
// CHECK FOR EXISTING
if([[NSFileManager defaultManager] fileExistsAtPath:[databaseFileURL path]]) {
// OPEN IF CLOSED
if([[self planetDatabase] documentState] == UIDocumentStateClosed) {
NSLog(#"Database: Closed");
[[self planetDatabase] openWithCompletionHandler:^(BOOL success) {
if(success)[self doWhatsNext];
}];
// USE IF NORMAL
} else if([[self planetDatabase] documentState] == UIDocumentStateNormal) {
[self doWhatsNext];
}
// CREATE AND OPEN
} else {
[[self planetDatabase] saveToURL: [[self planetDatabase] fileURL] forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
if(success)[self doWhatsNext];
}];
}
}
}
The first time I run this code from within Xcode the database is created "CREATE AND OPEN" the next time I run this code "OPEN IF CLOSED" is called. I assume this is correct, but am just trying to verify that Xcode does indeed close the open database between builds.
The SQLite database is not like, say MySQL. It's not a server running somewhere. It's just code inside your app. Hence, when you add a NSPersistenStore, the database is opened, and when your app shuts down, the database will be closed. That's how SQLite works.
Xcode doesn't do anything to your database. It's just your app touching it.
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
}