I am using https://www.tutorialspoint.com/objective_c/objective_c_file_handling.htm as a reference to move a file to another directory but it is not working. There is no error. I added the code to see if it could find the file and it can, but when I am using moveItemAtPath it does not seem to work.
NSFileManager *fileManager = [NSFileManager defaultManager];
//Get documents directory
NSArray *directoryPaths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectoryPath = [directoryPaths objectAtIndex:0];
if ([fileManager fileExistsAtPath:#"/Users/SAMPLE/folder1/file.txt"] == YES) {
NSLog(#"File exists");
}else{
NSLog(#"DNE");
}
NSError* error = nil;
if([fileManager moveItemAtPath:#"/Users/SAMPLE/folder1/file.txt"
toPath:#"/Users/SAMPLE/folder2/" error:&error]) {
NSLog(#"Moved successfully");
}
else if (error != nil) {
NSLog(#"%#", [error localizedDescription]);
}
I checked to see what the error is with code from https://stackoverflow.com/a/19244978/12486838 and the error message I get is
“file.txt” couldn’t be moved to “SAMPLE” because an item with the
same name already exists.
Why is it just stopping at Users/? Why is it comparing the second folder in the file path?
I have also tried to change toPath:#"/Users/SAMPLE/folder2/" to toPath:#"/Users/SAMPLE/folder2/file.txt" and the error message is
“file.txt” couldn’t be moved because you don’t have permission to access “folder2”.
What kind of permission would I need?
Related
I have been unable to delete the contents of a directory using NSDirectoryEnumerator.
Here is my method:
- (BOOL) deleteDirectoryContents:(NSString *)directoryPath
{
BOOL success = FALSE;
BOOL isDir;
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:directoryPath isDirectory:&isDir] && isDir)
{
NSDirectoryEnumerator *dirEnum = [fileManager enumeratorAtPath:directoryPath];
NSLog(#"dirEnum in deleteDirectoryContents is %#", dirEnum);
NSString *documentsName;
// NSLog(#"[dirEnum nextObject] is: %#", [dirEnum nextObject]);
while (documentsName = [dirEnum nextObject])
{
NSString *filePath = [directoryPath stringByAppendingString:documentsName];
NSLog(#"filePath is: %#", filePath);
BOOL isFileDeleted = [fileManager removeItemAtPath:filePath error:nil];
if(isFileDeleted == NO)
{
NSLog(#"All Contents not removed");
success = FALSE;
break;
}
success = TRUE;
}
if (success) NSLog(#"All Contents Removed");
}
return success;
}
And this is my code in my main program:
NSString *testDir = #"/Users/grinch/MyTestTemp";
//testDir = [NSHomeDirectory() stringByAppendingPathComponent: #"MyTestTemp"];
NSLog(#"testDir is: %#", testDir);
BOOL result = [self deleteDirectoryContents:testDir];
NSLog(#"result is: %d", result);
Here is my console output:
testDir is: /Users/grinch/MyTestTemp
dirEnum in deleteDirectoryContents is <NSAllDescendantPathsEnumerator: 0x60000008c300>
result is: 0
I also checked the value of [dirEnum nextObject] (by uncommenting the NSLog statement in my code). It returns null. And I never see the "filePath is" NSLog statement. So the inner while loop is NEVER executed.
And yes the directory (with files) does exist in my home directory. I created these files. I have permissions. I can easily delete the files in this directory using Finder
What am I missing?
P.S. I did some more testing. It looks like my simple program in Xcode does not have permissions to access files and folders in my home directory. Why? I have no idea.
Here is my additional test code:
NSError *errorMsg;
[[NSFileManager defaultManager] removeItemAtPath:#"/Users/grinch/myTestTemp/Hello.txt" error:&errorMsg];
if (errorMsg) NSLog(#"ERROR - File delete errorMsg is: %#", errorMsg);
success = [[NSFileManager defaultManager] removeItemAtPath:testDir error:&errorMsg];
if (errorMsg) NSLog(#"ERROR - Folder delete errorMsg is: %#", errorMsg);}
And here is my console output:
2022-09-10 09:56:48.958352-0400 NSAlert[97106:7511815] ERROR - File delete errorMsg is: Error Domain=NSCocoaErrorDomain Code=513 "“Hello.txt” couldn’t be removed because you don’t have permission to access it." UserInfo={NSFilePath=/Users/grinch/myTestTemp/Hello.txt, NSUserStringVariant=(
), NSUnderlyingError=0x6040006421f0 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
2022-09-10 09:56:48.960560-0400 NSAlert[97106:7511815] ERROR - Folder delete errorMsg is: Error Domain=NSCocoaErrorDomain Code=513 "“MyTestTemp” couldn’t be removed because you don’t have permission to access it." UserInfo={NSFilePath=/Users/grinch/MyTestTemp, NSUserStringVariant=(
), NSUnderlyingError=0x60400045ff80 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
So my questions are:
Why didn't the directory enumerator work?
Why doesn't Xcode have the permissions to delete items in my home folder?
How can I give Xcode the the ability (or permissions) to delete items in my home directory?
I have found a bug in my deleteDirectoryContents: method above. And as a result, have answered most of my questions!
First, the statement NSString *filePath = [directoryPath stringByAppendingString:documentsName]; in the while loop will not work. One must insert a / before documentsname. So the line should be NSString *filePath = [directoryPath stringByAppendingFormat:#"/%#",documentsName]; OR even better use NSString *filePath = [directoryPath stringByAppendingPathComponent:documentsName];.
Now the method deleteDirectoryContents: will work as expected.
Here is the code to the fixed method:
- (BOOL) deleteDirectoryContents:(NSString *)directoryPath
{
BOOL success = FALSE;
BOOL isDir;
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:directoryPath isDirectory:&isDir] && isDir)
{
NSDirectoryEnumerator *dirEnum = [fileManager enumeratorAtPath:directoryPath];
NSLog(#"dirEnum in deleteDirectoryContents is %#", dirEnum);
NSString *documentsName;
// NSLog(#"[dirEnum nextObject] is: %#", [dirEnum nextObject]);
while (documentsName = [dirEnum nextObject])
{
//NSString *filePath = [directoryPath stringByAppendingFormat:#"/%#",documentsName];
NSString *filePath = [directoryPath stringByAppendingPathComponent:documentsName];
NSLog(#"filePath is: %#", filePath);
NSError *errorMsg = nil;
BOOL isFileDeleted = [fileManager removeItemAtPath:filePath error:&errorMsg];
if (errorMsg) NSLog(#"ERROR - Failed to delete file or folder at: %#. Error message is: %#", filePath, errorMsg);
if(isFileDeleted == NO)
{
NSLog(#"All Contents not removed");
success = FALSE;
break;
}
success = TRUE;
}
if (success) NSLog(#"All Contents Removed");
}
return success;
}
And I can confirm that if your program creates the directory and the files within it, the program can also delete these files using deleteDirectoryContents: while running in the Xcode sandbox DEPENDING on where the folder was originally created.
If the program creates the folder and files in a temporary directory (e.g. caches directory) NOT the users home folder, it works.
But due to Xcode sandboxing,
deleteDirectoryContents: does not appear to work on folder and files created by the program in the users Home directory when running within Xcode.
P.S. I used the following to obtain a temporary directory name:
- (NSString *) get_temporary_dir
{
NSString *path = nil;
NSString *bundleName = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleIdentifier"];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
if ([paths count]) {
path = [[paths objectAtIndex:0] stringByAppendingPathComponent:bundleName];
} else {
path = NSTemporaryDirectory();
path = [path stringByAppendingPathComponent:bundleName];
}
return [[[NSString alloc] initWithUTF8String:[path UTF8String]] autorelease];
}
Then the program created the temporary directory using that name. The program created some files in that temporary directory and then was able to delete them using the deleteDirectoryContents: method above.
Problem: After long time running with no issues, my database is giving me a headache, it just wont stay at its place in the NSDocumentDirectory. The Database strangely disappears.
I never clear the documents-folder or delete anything. It only contains the database and saves some images in there which get downloaded if the user wants to keep them.
Has anybody an idea what could be going on?
after 3 days of struggling with this problem I can't come up with a possible solution, so please help me! :(
in my Database-Singleton I have the following init-Method
- (id)initWithName{
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:kDatabaseName];
//kDatabaseName = nameOfDatabase.db
[self checkAndCreateDatabase];
return self;
}
and the checkAndCreateDatabase - method:
- (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
NSURL *urlpath = [NSURL fileURLWithPath:databasePath];
NSError *error = nil;
[urlpath setResourceValue: [NSNumber numberWithBool: YES]
forKey: NSURLIsExcludedFromBackupKey error: &error];
success = [fileManager fileExistsAtPath:databasePath];
// If the database already exists then return without doing anything
if(success && sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
NSLog(#"database opened");
sqlite3_close(database);
return;
}
else{
sqlite3_close(database);
NSLog(#"Database was created");
}
// 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];
//NSString *databasePathFromApp = [[NSBundle mainBundle] pathForResource:databaseName ofType:#"sqlite3"];
// Copy the database from the package to the users filesystem
[fileManager copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
if ([self createTables]){ //creates tables of DB, works fine
NSLog(#"Tables were created");
} else {
NSLog(#"Database failed to create and open");
}
}
This code worked for a year straight. and suddenly when i needed to do some updates, the database was not saved anymore.
After a lot of troubleshooting I found out the database is being created in the Documents folder, but when i try to access the exact same path (cause i don't touch the variables) it disappears, with it's tables.
I tried different versions of my repository, all of them seem to have the problem. I really am getting mad. :(
Are you persisting the databasePath between app launches? In iOS 8 the DocumentsDirectory (and all the others, Caches, tmp, etc) became dynamic - - their name changes in between every app launch. So if you're storing the absolute path anywhere in your app it will be invalid the next time the app launches. If this is the case, a good way to fix it is to store the path relative to the DocumentsDirectory and whenever you need it just call
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
and append your path to that.
Help this helps.
I'm using the following snippet of code to attempt to copy a file from my application resources directory into the Documents area. I have used the below from the PocketOCR project on Github :
// Set up the tessdata path. This is included in the application bundle
// but is copied to the Documents directory on the first run.
NSString *dataPath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:#"tessdata"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSLog(#"Datapath is %#", dataPath);
// If the expected store doesn't exist, copy the default store.
if (![fileManager fileExistsAtPath:dataPath ]) {
NSLog(#"File didn't exist at datapath...");
// get the path to the app bundle (with the tessdata dir)
NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
NSString *tessdataPath = [bundlePath stringByAppendingPathComponent:#"tessdata"];
if (tessdataPath) {
[fileManager copyItemAtPath:tessdataPath toPath:dataPath error:NULL];
}
}
This is the output I get from invoking the above :
2012-06-06 14:53:42.607 MyApp[1072:707] Datapath is
/var/mobile/Applications/9676D920-D6D1-4F86-9177-07CC3247A124/Documents/tessdata
Error opening data file
/var/mobile/Applications/9676D920-D6D1-4F86-9177-07CC3247A124/Documents/tessdata/eng.traineddata
I have the eng.traineddata file located in my xcode project like so
It appears that the Error is being reported when attempting to check if fileExistsAtPath, I wouldn't expect this to throw an error, but to either return YES or NO.
Is there an easier way to copy the eng.traineddata file across, or have I made a mistake which can be corrected here?
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSString *dataPath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:#"tessdata"];
NSLog(#"Datapath is %#", dataPath);
// If the expected store doesn't exist, copy the default store.
if ([fileManager fileExistsAtPath:dataPath] == NO)
{
NSString *tessdataPath = [[NSBundle mainBundle] pathForResource:#"eng" ofType:#"traineddata"];
[fileManager copyItemAtPath:tessdataPath toPath:dataPath error:&error];
}
-(NSString*) applicationDocumentsDirectory{
// Get the documents directory
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [dirPaths objectAtIndex:0];
return docsDir;
}
I hope also help this code to copy all type of file bundle to Document directory folder ..
NSString *pathsToReources = [[NSBundle mainBundle] resourcePath];
NSString *yourOriginalDatabasePath = [pathsToReources stringByAppendingPathComponent:#"lunchDB.sqlite"]; // file name u want copy
NSArray *pathsToDocuments = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [pathsToDocuments objectAtIndex: 0];
NSString *dbPath = [documentsDirectory stringByAppendingPathComponent:#"lunchDB.sqlite"]; // file name & your original path (Document path)
if (![[NSFileManager defaultManager] isReadableFileAtPath: dbPath]) {
if ([[NSFileManager defaultManager] copyItemAtPath: yourOriginalDatabasePath toPath: dbPath error: NULL] != YES)
NSAssert2(0, #"Fail to copy database from %# to %#", yourOriginalDatabasePath, dbPath);
}
Thanks ..
Use the method fileExistsAtPath:isDirectory: of NSFileManager and specify that the path is a directory, because it is.
stringByAppendingPathComponent:#"tessdata"
The line above required that tessdata is a physical folder (a blue color folder), not a group (yellow folder as you are showing) in Xcode project. Create a folder in your Xcode project folder using the Finder and then in Xcode drag and drop the folder to your project and select the folder reference when asked.
I'm trying to create database file in first application's launch. Used FMDB library. For this purpose I'm checking if database file already exist, if not I want to copy database file from project.
My code:
-(void)checkAndCreateDatabase {
NSLog(#"check and create database doing");
NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *dbFileName = [docDir stringByAppendingPathComponent:#"FriendsDatabase.sqlite3"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
if (![fileManager fileExistsAtPath:dbFileName]) {
[fileManager copyItemAtPath:[[NSBundle mainBundle] pathForResource:#"FriendsDatabase" ofType:#"sqlite3"] toPath:dbFileName error:&error];
NSLog(#"database created");
} else {
NSLog(#"fail to create database");
}
FMDatabase *_db = [[FMDatabase alloc] initWithPath: dbFileName];
if (![_db open]) {
[_db release];
NSLog(#"Could't open DB");
}
_db.logsErrors = NO;
self.db = _db;
[_db release];
}
But I have an error: 'NSInvalidArgumentException', reason: '*** -[NSFileManager copyItemAtPath:toPath:error:]: source path is nil'. What I'm doing wrong?
It looks like you don't have a file named "FriendsDatabase.sqlite3" in your main bundle. (If you had one, the source path in your call to -copyItemAtPath:toPath:error: wouldn't be nil.)
Try changing your code to look like:
NSString *path = [[NSBundle mainBundle] pathForResource:#"FriendsDatabase" ofType:#"sqlite3"];
[fileManager copyItemAtPath:path toPath:dbFileName error:&error];
Then step through the code with your debugger and check the value of path.
Note that you've specified "FriendsDatabase" (note the 's') as the name of the file in your code, but your comment calls the file "FriendDatabase". Perhaps you've just misspelled the filename in your code -- that's a good reason to use constants!
Caleb's answer is correct, but I'd add that you have to make sure that the sqlite3 file is a member of your target. In XCode, select your sqlite3 file, then open the "File Inspector" panel (right panel) under "Target Membership". Make sure your target app is checked.
I have been trying to create a new file inside of my application support folder while using NSApplicationSupportDirectory; I can write a file to it, but I have been unable to create a folder inside of Application Support.
NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSApplicationSupportDirectory, NSUserDomainMask, YES);
NSString *applicationDirectory = [paths objectAtIndex:0];
//make a file name to write the data to using the application support: (attempting to create the blasted directory inside of application support directory
NSString *fileName = [NSString stringWithFormat:#"%#/managersemail.txt",
applicationDirectory];
//create content - formats with the managersemail.txt location
NSString* content = [NSString stringWithFormat:#"%#",[nameField stringValue]];
//save content to the documents directory
[content writeToFile:fileName
atomically:NO
encoding:NSStringEncodingConversionAllowLossy
error:nil];
NSDictionary* errorDict;
The code that I have listed above works great, except for the part about creating the folder in which I want to place the managersemail.txt. I tried to mimic the stringWithFormat that is listed in the NSString* content and then varying several ways however to no avail! Any thoughts?
NSAppleEventDescriptor* returnDescriptor = NULL;
Perhaps the solution provided on Cocoa with Love might be useful?
Excerpt:
- (NSString *)findOrCreateDirectory:(NSSearchPathDirectory)searchPathDirectory
inDomain:(NSSearchPathDomainMask)domainMask
appendPathComponent:(NSString *)appendComponent
error:(NSError **)errorOut
{
// Search for the path
NSArray* paths = NSSearchPathForDirectoriesInDomains(
searchPathDirectory,
domainMask,
YES);
if ([paths count] == 0)
{
// *** creation and return of error object omitted for space
return nil;
}
// Normally only need the first path
NSString *resolvedPath = [paths objectAtIndex:0];
if (appendComponent)
{
resolvedPath = [resolvedPath
stringByAppendingPathComponent:appendComponent];
}
// Check if the path exists
BOOL exists;
BOOL isDirectory;
exists = [self
fileExistsAtPath:resolvedPath
isDirectory:&isDirectory];
if (!exists || !isDirectory)
{
if (exists)
{
// *** creation and return of error object omitted for space
return nil;
}
// Create the path if it doesn't exist
NSError *error;
BOOL success = [self
createDirectoryAtPath:resolvedPath
withIntermediateDirectories:YES
attributes:nil
error:&error];
if (!success)
{
if (errorOut)
{
*errorOut = error;
}
return nil;
}
}
if (errorOut)
{
*errorOut = nil;
}
return resolvedPath;
}
Maybe you can try using the NSFileManager to create the folder, then write the file into the folder.
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *applicationSupport = [[NSString stringWithString:#"~/Library/Application Support/'YOUR APP'] stringByExpandingTildeInPath];
if ([fileManager fileExistsAtPath:applicationSupport] == NO)
[fileManager createDirectoryAtPath:applicationSupport withIntermediateDirectories:YES attributes:nil error:nil];
NSString *fileName = [NSString stringWithFormat:#"%#/managersemail.txt", applicationSupport];
NSString* content = [NSString stringWithFormat:#"%#",[nameField stringValue]];
//save content to the documents directory
[content writeToFile:fileName
atomically:NO
encoding:NSStringEncodingConversionAllowLossy
error:nil];
So something like that should work. Feel free to leave comments to ask questions.