Error when trying to deleting a file that exist - iphone

A tester said that he got this message only once, and I can't seem to reproduce it or figure out how this message appeared. In a nut shell, my application starts a background thread and grabs data from our server. Once we are done downloading the data we store it in an xml file, so we can grab the data from this file incase we shutdown durning the parsing of the xml. Once I am done parsing the xml I delete the file. Here is my code for deleting.
-(void)deleteSavedXmlServerData
{
AppDelegate_Shared* appDelegate = (AppDelegate_Shared*)[[UIApplication sharedApplication] delegate];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *fileToLoad = [NSString stringWithFormat:#"%#/%#/upload.xml", [paths objectAtIndex:0], appDelegate.userId];
if([[NSFileManager defaultManager] fileExistsAtPath:fileToLoad] == YES)
{
NSError *error = nil;
[[NSFileManager defaultManager] removeItemAtPath:fileToLoad error:&error];
if(error != nil)
{
eNSLog(#"%s Error Parsing old data: %# info: %#", __PRETTY_FUNCTION__, error, [error userInfo]);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Problem With Syncing"
message:#"Problem reading in old data."
delegate:nil
cancelButtonTitle:#"ok"
otherButtonTitles:nil];
[alert show];
[alert release];
}
}
}
Everything seems to work perfectly, and it is not a big deal if this file does not get deleted. But I am confused why I would have an issue with deleting this file.
When I checked what the error message was this is what I got this.
-[ServerSync deleteSavedXmlServerData] Error Parsing old data: Error Domain=NSCocoaErrorDomain Code=4 UserInfo=0x11f3ed60 "Operation could not be completed. (Cocoa error 4.)" info: {
NSFilePath = "/var/mobile/Applications/CD181518-512F-4A0E-82ED-C438886E4A1D/Documents/71BD9A11-A604-6001-549C-DF6582F60124/upload.xml";
NSUserStringVariant = Remove;
}
If anyone know why if my file exist and I created it, why I cannot delete it?
Update
The question that confused me the most, is why does the first if statement say the file exist, but the error message that I get from trying to remove the file says that my file does not exist?

Reading the documentation for NSFileManager (don't know if I just missed it the first time), it says to not use the singleton method [NSFileManager defaultManager] because NSFileManager is not thread safe. I have not had this problem since then.

It looks like the document directory path is /.../71BD9A11-A604-6001-549C-DF6582F60124CD181518-512F-4A0E-82ED-C438886E4A1D/Documents/71BD9A11-A604-6001-549C-DF6582F60124. It should end in /Documents.
Googling for NSCocoaErrorDomain gives NSFileNoSuchFileError = 4.

Testing for file existence before removing it suffers from a race condition as another thread can come in and remove it before the deletion code executes. It would be better to refactor your code to remove the file first and ignore any "errors" related to file not found.
BTW: the answer regarding [NSFileManager defaultManager] not being thread safe is not entirely true. It is thread safe except when using a delegate for notifications.

According to this answer
NSFileManager: removing item
we need to have a file:// prefix. You can use [NSURL fileURLWithPath:] to achieve it.
For swift use NSURL(fileURLWithPath: realmPath)

Related

Parsing Local XML File Works, Downloaded XML File Doesn't

I have this problem when I fetch an XML file from the internet and then parse it, where I get this error:
Error while parsing the document: Error Domain=SMXMLDocumentErrorDomain Code=1 "Malformed XML document. Error at line 1:1." UserInfo=0x886e880 {LineNumber=1, ColumnNumber=1, NSLocalizedDescription=Malformed XML document. Error at line 1:1., NSUnderlyingError=0x886e7c0 "The operation couldn’t be completed. (NSXMLParserErrorDomain error 5.)"}
Here is an extract from the code (I believe I am only showing the most relevant code, if you need more, please ask.)
// Create a URL Request and set the URL
NSURL *url = [NSURL URLWithString:#"http://***.xml"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// Display the network activity indicator
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
// Perform the request on a new thread so we don't block the UI
dispatch_queue_t downloadQueue = dispatch_queue_create("Download queue", NULL);
dispatch_async(downloadQueue, ^{
NSError* err = nil;
NSHTTPURLResponse* rsp = nil;
// Perform the request synchronously on this thread
NSData *rspData = [NSURLConnection sendSynchronousRequest:request returningResponse:&rsp error:&err];
// Once a response is received, handle it on the main thread in case we do any UI updates
dispatch_async(dispatch_get_main_queue(), ^{
// Hide the network activity indicator
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
if (rspData == nil || (err != nil && [err code] != noErr)) {
// If there was a no data received, or an error...
NSLog(#"No data received.");
} else {
// Cache the file in the cache directory
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString* path = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"init.xml"];
//NSLog(#"%#",path);
[[NSFileManager defaultManager] removeItemAtPath:path error:nil];
[data writeToFile:path atomically:YES];
//NSString *sampleXML = [[NSBundle mainBundle] pathForResource:#"sample" ofType:#"xml"];
NSData *data = [NSData dataWithContentsOfFile:path];
// create a new SMXMLDocument with the contents of sample.xml
NSError *error;
SMXMLDocument *document = [SMXMLDocument documentWithData:data error:&error];
// check for errors
if (error) {
NSLog(#"Error while parsing the document: %#", error);
// return;
}
Firstly, I have connected the iPhone to an XML feed which it has fetched and written to the path of the variable path. Then I check for errors in the XML document and I get that error every time.
However, if I use a local XML file which I have placed in the main folder of my application there is no problem fetching all the data.
Using the code:
NSString *sampleXML = [[NSBundle mainBundle] pathForResource:#"sample" ofType:#"xml"];
So does anyone have an idea as to what I can have done wrong? It seems as if it doesn't download and store the XML file to the iPhone's cache, however NSLog(); seems to show it differently. Obviously the local file is the same as the file on the internet.
Furthermore, I already tried to save the file to the path without any results, though.
A couple of observations:
The key issue would appear to be that you retrieved the data in rspData, but when you write it to your temporary file, you're writing data, not rspData. So change the line that says:
[data writeToFile:path atomically:YES];
to
[rspData writeToFile:path atomically:YES];
Frankly, I don't even see the data variable defined at that point (do you have some ivar lingering about?). I'd be ruthless about getting rid of any ivars or other variables that you don't need, so that you don't accidentally refer to some unused variable. Anyway, just use the rspData that you retrieved rather than some other variable.
Why are you even writing that to a file, only to then read the file into another NSData that you pass to your XML parser? That seems entirely unnecessary. Just go ahead and use the rspData you initially retrieved. If you want to save the NSData to a file so you can examine it later for debugging purposes, that's fine. But there's no point in re-retrieving the NSData from the file, as you already have it in a rspData already.
If you encounter these errors in the future, feel free to examine the contents of the NSData variable with a debugging line of code, something like:
NSLog(#"rspData = %#", [[NSString alloc] initWithData:rspData encoding:NSUTF8StringEncoding]);
When you do that, you can look at the string rendition of the NSData, and usually the problem will become self evident.
As a complete aside, in your debugging error handler, you have a line that says:
NSLog(#"No data received.");
I might suggest you always include any errors that might be provided, e.g.:
NSLog(#"No data received: error = %#", err);
iOS provides useful error messages, so you should avail yourself of those.

Odd NSString Behavior "warning: Unable to read symbols for..."

I have an object that handles database actions. It initiates with:
-(id)init{
databaseName = #"WhoPaidLast.sql";
// I think this one gets it from the app whereas the next one gets it from the phone
// databasePath = [[NSBundle mainBundle] pathForResource:#"WhoPaidLast" ofType:#"sql"];
// Get the path to the documents directory and append the databaseName
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
self.databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
// Execute the "checkAndCreateDatabase" function
[self checkAndCreateDatabase];
return self;
At the end of this I check the db:
-(void) checkAndCreateDatabase{
// Check if the SQL database has already been saved to the users phone, if not then copy it over
BOOL success;
// Create a FileManager object, we will use this to check the status
// of the database and to copy it over if required
NSFileManager *fileManager = [NSFileManager defaultManager];
// Check if the database has already been created in the users filesystem
success = [fileManager fileExistsAtPath:self.databasePath];
// If the database already exists then return without doing anything
if(success) return;
// If not then proceed to copy the database from the application to the users filesystem
// Get the path to the database in the application package
NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:databaseName];
// Copy the database from the package to the users filesystem
[fileManager copyItemAtPath:databasePathFromApp toPath:self.databasePath error:nil];
//[fileManager release];
}
After this, when I go to use databasePath I only seem to be able to use it once before it throws this error:
warning: Unable to read symbols for /Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.3.1 (8G4)/Symbols/Developer/usr/lib/libXcodeDebuggerSupport.dylib (file not found).
I have a function that returns a bunch of values from the database. The first time detabasePath is used it works fine and outputs the intended value, the second time it throws the above error.
Here is the beginning of that function:
// Setup the database object
sqlite3 *database;
// Init groupsArray
groupsArray = [[NSMutableArray alloc] init];
NSLog(#"r- %#",self.databasePath);
NSLog(#"s- %#",self.databasePath);
// Open the database from the users filessytem
if(sqlite3_open([self.databasePath UTF8String], &database) == SQLITE_OK) {
If I remove the second NSLog function then it errors the next time databasePath is used. If I remove both it'll work for the sqlite3_open function but error on the next use.
If anyone knows the source of my error and/or what I might be doing wrong I'd very much appreciate your help.
Thanks.
There look to be a number of memory-management-related errors in the above code. I'm guessing that the debugger error you're seeing is due to a memory-related crash that the debugger isn't quite able to latch onto to give you a stack trace.
First off, your -init method never calls this on the superclass, which is not right. You need to have something like
if (!(self = [super init]))
{
return nil;
}
at the start of that method.
That alone should lead to all kinds of fun crashes. Beyond that, make sure that the databasePath property is set to use copy or retain (preferably copy, given that it's an NSString), or it won't hold on to the autoreleased object returned by [documentsDir stringByAppendingPathComponent:databaseName].
Also, I know you have it commented out, but don't use [fileManager release]. That's a shared instance you get back from [NSFileManager defaultManager] and you haven't retained it, so releasing that would lead to bad things.
You could try to enable breakpoints and turn NSZombie on to find your specific crash location, but I bet the cause is one of the factors I list above.
This is a bug of iOS/ Xcode 4
Enter the following 2 commands in Terminal
cd /Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.3.1\ \(8G4\)/Symbols/
sudo ln -s /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/Developer/ Developer
Clean (shift+cmd+k) the project before run it. And the problem should solved.
Ref: http://blog.damore.it/2011/04/xcode4-debugging-problem-warning-unable.html

When is the iPhone app cache is cleared?

I'm working on an app that lets users record voice (among other things) to the Documents directory of the app. But when I'm recording the voice, I'm recording to the caches directory of the app and then after the user says "Okay, save this one", then I'm coping it to the Documents directory. So far all these work. But if I try to delete the data file in cache, or when I try to move it, I get problems.
So my question is, shall I just leave the data in cache so that iOS will handle it or do I need to manually delete the files in cache. If so how would I go about doing it. This is the code I have so far (which doesn't work)
NSFileManager *fm = [NSFileManager defaultManager];
NSString *directory = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSError *error = nil;
BOOL success = [fm removeItemAtPath:[NSString stringWithFormat:#"%#%#", directory, currentEntry.audioFileURL] error:&error];
if (!success || error) {
// it failed.
NSLog(#"it failed to delete!!! %# %#", error, [error userInfo]);
} else {
NSLog(#"Deleted... yipee... !!!");
}
I think the problem is that your path is not correct. Always use the
- (NSString *)stringByAppendingPathComponent:(NSString *)aString
method of NSString.
You can try printing out the path you get now (maybe a backslash is missing), but anyway you should use the method I described.
UPD: Another thing is that NSCachesDirectory is actually never cleaned up. Use NSTemporaryDirectory() if you want automatic cleaning.

How to directly store SQLite data in XCode?

Searched on here and got some vague answers, so I thought i'd rephrase the problem to get some clearer answers-
Right now I have an SQL Lite db that reads/parses information from a pre-formatted .txt file. When I open the app, there is a slight 'lag' as the iDevice parses the info, then gets fetched for the iDevice. I'm just wondering if there's any way to just 'save' all the information directly in the xCode so there's no lag/fetch time?
Thank you.
What you can do is pre-build your sqlite database and then include it as a resource in your application. As this database is inside your application bundle it will be read only on your device so you will need to make a copy of it in your application document area.
I have successfully used something like the following code in a production iPhone application
NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentFolderPath = [searchPaths objectAtIndex: 0];
NSString* dbFilePath = [documentFolderPath stringByAppendingPathComponent: DATABASE_FILE_NAME];
if(![[NSFileManager defaultManager] fileExistsAtPath: dbFilePath]) {
// copy the template database into the right place
NSError* error = nil;
[[NSFileManager defaultManager] copyItemAtPath: [[NSBundle mainBundle] pathForResource: #"template" ofType: #"db"] toPath: dbFilePath error: &error];
if(error) {
#throw [NSException exceptionWithName: #"DB Error" reason: [error localizedDescription] userInfo: nil];
}
}
int dbrc; // database return code
dbrc = sqlite3_open ([dbFilePath UTF8String], &db);
if(IS_SQL_ERROR(dbrc)) {
#throw [NSException exceptionWithName: #"DB Error" reason: NSLocalizedString(#"Could not open database", #"Database open failed") userInfo: nil];
}

How to force coredata to rebuild sqlite database model?

In my app I sometimes need to rebuild and repopulate database file. SQLite databse is created and managed by CoreData stack.
What I'm trying to do is drop the file and then simply recreate persistentStoreCoordinator object.
It works under simulator but not on device, where I'm getting such an error:
NSFilePath = "/var/mobile/Applications/936C6CC7-423A-46F4-ADC0-7184EAB0CADD/Documents/MYDB.sqlite";
NSUnderlyingException = I/O error for database at /var/mobile/Applications/936C6CC7-423A-46F4-ADC0-7184EAB0CADD/Documents/MYDB.sqlite. SQLite error code:1, 'table ZXXXX already exists';
I cannot find the cause of this in any way. It indicates two different problems - Cocoa error 256 indicates that file does not exist or is not readable. But file IS created after creating persistenStoreCoordinator, although it's empty, but after executing some queries it disappears.
Second message indicating attempt to create alredy existing table is quite strange in that case.
I'm quite confused and cannot get the point what's going on here. My code looks like this:
NSString *path = [[WLLocalService dataStorePath] relativePath];
NSError *error = nil;
WLLOG(#"About to remove file %#", path);
[[NSFileManager defaultManager] removeItemAtPath: path error: &error];
if (error != nil) {
WLLOG(#"Error removing the DB: %#", error);
}
[self persistentStoreCoordinator];
WLLOG(#"Rebuild DB result %d", [[NSFileManager defaultManager] fileExistsAtPath: path]);
After this code is exectued, DB file exists but is empty. When then first query (and all following) is executed, it gives me the error above and file disappears.
Does anybody has an idea what's wrong with it?
Big thanks for pointing me the right way!
The Core Data stack does not like you removing the file under it. If you are wanting to delete the file you should tear down the stack, delete the file and then reconstruct the stack. That will eliminate the issue.
Part of the problem is that the stack keeps a cache of the data that is in the file. When you remove the file you don't have a way to clear that cache and you are then putting Core Data into an unknown and unstable state.
You can try telling the NSPersistentStoreCoordinator you are removing the file with a call to -removePersistentStore:error: and then adding the new store with a call to -addPersistentStoreWithType:configuration:URL:options:error:. I am doing that currently in ZSync and it works just fine.
I use the following method -resetApplicationModel in my app delegate and it works fine for me.
You may not need the kApplicationIsFirstTimeRunKey user default, but I use it to test whether to populate the Core Data store with default settings in a custom method called -setupModelDefaults, which I also call from -applicationDidFinishLaunching: if the first-time run flag is YES.
- (BOOL) resetApplicationModel {
// ----------------------
// This method removes all traces of the Core Data store and then resets the application defaults
// ----------------------
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:YES] forKey:kApplicationIsFirstTimeRunKey];
NSLog(#"Turned ON the first-time run flag...");
NSError *_error = nil;
NSURL *_storeURL = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"MyAppSQLStore.sqlite"]];
NSPersistentStore *_store = [persistentStoreCoordinator persistentStoreForURL:_storeURL];
//
// Remove the SQL store and the file associated with it
//
if ([persistentStoreCoordinator removePersistentStore:_store error:&_error]) {
[[NSFileManager defaultManager] removeItemAtPath:_storeURL.path error:&_error];
}
if (_error) {
NSLog(#"Failed to remove persistent 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]);
}
return NO;
}
[persistentStoreCoordinator release], persistentStoreCoordinator = nil;
[managedObjectContext release], managedObjectContext = nil;
//
// Rebuild the application's managed object context
//
[self managedObjectContext];
//
// Repopulate Core Data defaults
//
[self setupModelDefaults];
return YES;
}
You can keep a "clean" copy of your sqlite database as part of the application bundle, then just copy over the version in the documents directory whenever you'd like to refresh the database.
Here's some code from an App that does something similar (although this version will not copy over and existing db):
// Check for the existence of the seed database
// Get the path to the documents directory and append the databaseName
NSString* databasePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: kDatabaseName];
NSFileManager* fileManager = [NSFileManager defaultManager];
if ( ![fileManager fileExistsAtPath: databasePath] )
{
NSString* databasePathFromApp = [[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent: kDatabaseName];
[fileManager copyItemAtPath: databasePathFromApp
toPath: databasePath
error: nil];
}
[fileManager release];