Copy Directories With copyItemAtPath? - objective-c

I am trying to copy the directory "DATA_001" (and its contents) into the directory "CRYO". I was under the impression that I could do this using copyItemAtPath like I would for a file? Is this the wrong way to be doing this?
NSString *sourceDir = #"/Users/Fuzzygoat/Documents/DATA_001";
NSString *destDir = #"/Users/Fuzzygoat/Documents/CRYO";
NSString *sourceFile = #"/Users/Fuzzygoat/Documents/DATA_001/caroline.png";
NSString *destFile = #"/Users/Fuzzygoat/Documents/CRYO/cjg.png";
// COPY DIR
success = [fileManager copyItemAtPath:sourceDir toPath:destDir error:&dError];
if(success != YES) NSLog(#"Error");
// COPY FILE
success = [fileManager copyItemAtPath:sourceFile toPath:destFile error:&fError];
if(success != YES) NSLog(#"Error");
gary

If you copy a directory all of the contents are recursively copyed, which means your second call to copy is completely superfluous, you can just do this:
NSString *sourceDir = #"/Users/Fuzzygoat/Documents/DATA_001";
NSString *destDir = #"/Users/Fuzzygoat/Documents/CRYO";
// COPY DIR
success = [fileManager copyItemAtPath:sourceDir toPath:destDir error:&dError];
if(success != YES) NSLog(#"Error: %#", dError);
Obviously if you don't want to copy everything in the directory you should not copy the directory itself, and instead only copy the entries you want.
I should note that you didn't specify that you were having any particular problems. There are obviously a number of reasons this sort of thing can fail (permissions, issues, etc), which should be indicated by the value of dError. If you are asking this question because you have been getting unexpected results you need to include more details about what is going on and how it differs from your expectations.

Related

moveItemAtPath. No errors, but not working

Im trying to move files. Below I'm testing if the paths exist. They do, however both copyItemAtPath and moveItemAtPath don't seem to work.
NSString *testUrl = #"/Users/justinshulman/Documents/test2";
if ([[NSFileManager defaultManager]fileExistsAtPath:testUrl]) {
NSLog(#"yes");
}
NSString *testUrl2 = #"/Users/justinshulman/Documents/test1";
if ([[NSFileManager defaultManager]fileExistsAtPath:testUrl2]) {
NSLog(#"yes");
}
NSLog(#"%#",testUrl);
NSLog(#"%#",testUrl2);
[[NSFileManager defaultManager]copyItemAtPath:testUrl2 toPath:testUrl error:nil];
[[NSFileManager defaultManager]moveItemAtPath:testUrl2 toPath:testUrl error:nil];
That is exactly your problem, both move and copy will not actually overwrite the destination file if it already exists. You'll have to remove it first and then copy (or move) the other file to that URL.
Try with
[[NSFileManager defaultManager] removeItemAtPath:testUrl error:nil];
[[NSFileManager defaultManager]copyItemAtPath:testUrl2 toPath:testUrl error:nil];
and it should work fine.
You should also be checking for the error instead of passing nil.
NSError* error = nil;
[[NSFileManager defaultManager]copyItemAtPath:testUrl2 toPath:testUrl error:&error];
if (error != nil) {
NSLog(#"%#", [error localizedDescription]);
}
It also returns a bool on whether the copy was successful.
Adding to #micantox answer, always read the class reference. See class reference for NSFileManager:
If a file with the same name already exists at dstPath, this method
aborts the copy attempt and returns an appropriate error.
You should pass NSError object in error filed.
[[NSFileManager defaultManager]copyItemAtPath:testUrl2 toPath:testUrl error:&error];
Error Domain=NSPOSIXErrorDomain Code=17 UserInfo=0x100457e80 "The operation couldn’t be completed.
[[NSFileManager defaultManager]moveItemAtPath:testUrl2 toPath:testUrl error:&error];
Error Domain=NSCocoaErrorDomain Code=512 UserInfo=0x1004a2270
Use replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error:
Replaces the contents specified by the first URL with the contents of
the second URL in a manner that insures no data loss occurs.
#justin, First thing it never works only. Because you are trying to copy the source path to destination path where both path are same. Second thing, how NSFileManager copy or move api works is, you have to copy or move source path to different destination path with appending your appropriate path component. For example see the code below:--
NSString *testUrl = #"/Users/home/Documents/source.rtf";
//
if ([[NSFileManager defaultManager]fileExistsAtPath:testUrl]) {
NSLog(#"yes");
}
//Below destination is folder name which should be exist on your machine or else you can create programmatically as well
NSString *testUrl2 = #"/Users/home/Documents/destination";
NSLog(#"%#",testUrl);
NSLog(#"%#",testUrl2);
NSError *err=nil;
//Now we are copying the souce path to destination folder with appending file name (it can be any your name becuase file manager copy source file contents to your destination file contents)
//Here given file name is a destination.rtf where you can give any your name. Also this is for copying source contents to destination contents
NSFileManager *fm=[NSFileManager defaultManager];
if ([fm copyItemAtPath:testUrl toPath:[testUrl2 stringByAppendingPathComponent:#"destination.rtf"] error:&err])
{
NSLog(#"success");
}
else
{
NSLog(#"%#",[err localizedDescription]);
}

NSFileManager copyItemAtPath complains about a nonexistent file that does exist

I am trying to copy a file using [[NSFileManager defaultManager] copyItemAtPath: toPath: error:] but it is failing with the following error:
4: The file does not exist.
The relevant code is below, and the file does exist and the path string is correct because it is created beforehand with the exact same file path string.
NSFileManager* manager = [NSFileManager defaultManager];
NSError* error;
NSString* fileName = [Sound getFileName:Title];
NSString* oldDirectory = [NSString stringWithFormat:#"%#%#/", [settings stringForKey:#"downloadFolder"], authorFolder];
NSString* oldFile = [oldDirectory stringByAppendingFormat:#"%#.mp3", fileName];
NSString* newFile = [NSString stringWithFormat:#"%#/iTunes/iTunes Media/Automatically Add to iTunes/%#.mp3", [NSSearchPathForDirectoriesInDomains(NSMusicDirectory, NSUserDomainMask, YES) objectAtIndex:0], fileName];
BOOL result = [manager copyItemAtPath:oldFile toPath:newFile error:&error];
if (!result && error)
{
NSLog(oldFile);
NSLog(#"There was an error copying the file to the iTunes directory! %#", [error localizedDescription]);
}
It's not the exact code, but all relevant code should be above. If I use [manager fileExistsAtPath:oldFile] the result is YES.
What could cause the copy to fail and say the file doesn't exist, even if it does?
UPDATE:
Issue fixed. Turns out the output folder was really Automatically Add to iTunes.localized, but I didn't notice this initially when just paging through the finder. Fixing the output path solved the issue! Thanks for the help.
If any of the directories in the path of the destination don't exist, you'll get a similar error to what you'd get if the source doesn't exist. Check what [manager fileExistsAtPath:[newFile stringByDeletingLastPathComponent] isDirectory:&isDir] returns.
You're using the API wrong. You need to look at the return value of -copyItemAtPath:toPath:error:. Only if that returns NO does that mean an error occurred.
If you're using ARC, your error variable should be nil if no error occurred (although this isn't technically guaranteed), but if you're using MRR it probably won't, because you never initialized it.

How to make a directory iOS?

Okay,
So I have a Cydia app that I need to update. I am aware with Cydia apps that they don't have a Documents folder, so you have to make one. And here's how I made it before in iOS 4 (which doesn't work on iOS 5):
mkdir("/var/mobile/Library/APPNAME", 0755);
mkdir("/var/mobile/Library/APPNAME/Documents", 0755);
NSString *foofile = #"/var/mobile/Library/APPNAME/Documents/database.db";
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:foofile];
if (fileExists == TRUE) {
NSLog(#"already exists");
} else {
NSLog(#"doesn't exists");
NSFileManager *fileManager = [[NSFileManager defaultManager]autorelease];
NSError *error;
NSString *documentDBFolderPath = #"/var/mobile/Library/APPNAME/Documents/database.db";
NSString *resourceDBFolderPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"database.db"];
[fileManager copyItemAtPath:resourceDBFolderPath toPath:documentDBFolderPath error:&error];
}
I also included code that copies the database file to that folder, too. That doesn't work (even when I create the folder manually via SSH).
Please help! Thanks.
Here is the method I made to create directories
-(void)createDirectory:(NSString *)directoryName atFilePath:(NSString *)filePath
{
NSString *filePathAndDirectory = [filePath stringByAppendingPathComponent:directoryName];
NSError *error;
if (![[NSFileManager defaultManager] createDirectoryAtPath:filePathAndDirectory
withIntermediateDirectories:NO
attributes:nil
error:&error])
{
NSLog(#"Create directory error: %#", error);
}
}
Try using createDirectoryAtURL:withIntermediateDirectories:attributes:error:.
NSFileManager Class Reference:
createDirectoryAtURL:withIntermediateDirectories:attributes:error:
Creates a directory with given attributes at the specified path.
Parameters
url - A file URL that specifies the directory to create.
If you want to specify a relative path, you must set the
current working directory before creating the corresponding
NSURL object. This parameter must not be nil.
createIntermediates - If YES, this method creates any non-existent
parent directories as part of creating the directory in url. If NO,
this method fails if any of the intermediate parent directories does
not exist. This method also fails if any of the intermediate path
elements corresponds to a file and not a directory.
attributes - The file attributes for the new directory and any newly created
intermediate directories. You can set the owner and group numbers,
file permissions, and modification date. If you specify nil for this
parameter or omit a particular value, one or more default values are
used as described in the discussion. For a list of keys you can
include in this dictionary, see “Constants” (page 54) section lists
the global constants used as keys in the attributes dictionary. Some
of the keys, such as NSFileHFSCreatorCode and NSFileHFSTypeCode, do
not apply to directories.
error - On input, a pointer to an error object. If an error occurs,
this pointer is set to an actual error object containing the error
information. You may specify nil for this parameter if you do not
want the error information.
Return Value
YES if the
directory was created or already exists or NO if an error occurred.
Check NSFileManager's class reference. To create folders you need createDirectoryAtPath:withIntermediateDirectories:attributes:error:
Superb Techotopia explanation of iOS5 filesystem
In Swift, returns true if exists or created.
func ensureDirectoryExists(path:String) -> Bool {
if !NSFileManager.defaultManager().fileExistsAtPath(path) {
do {
try NSFileManager.defaultManager().createDirectoryAtPath(path, withIntermediateDirectories: true, attributes: nil)
} catch {
print(error)
return false
}
}
return true
}

FMDB - Failed to modify DB from Cocoa

I am working on a Cocoa application which talks to a local SQLite database with FMDB. I ran into an issue that I can't do any insert or update operation on DB. Select queries run perfectly fine, so I would assume my db connection settings are correct.
The structure of my code is basically like this:
FMDatabase* db=[FMDatabase databaseWithPath:[[NSBundle mainBundle] pathForResource:#"DBName" ofType:#"sqlite"]];
if(![db open])
{
NSLog(#"Could not open db.");
}
db.traceExecution=YES;
[db beginTransation];
[db ExecuteUpdate:"INSERT INTO test (title) VALUES(?)", [NSNumber numberWithInt]:2],nil];
[db commit];
[db close];
No exceptions or warnings were thrown during execution, the console output regarding db.traceExecution is like following:
<FMDatabase: 0x100511fd0> executeUpdate: BEGIN EXCLUSIVE TRANSACTION;
<FMDatabase: 0x100511fd0> executeUpdate: INSERT INTO test (title) VALUES(?);
obj: 2
<FMDatabase: 0x100511fd0> executeUpdate: COMMIT TRANSACTION;
The testing database is simply just a one column table of INT type.
Everything looks fine except that the db file is not updated at all. It's really confusing to me as the Select query works perfectly fine. I checked the path of the database, it is pointing to the right one. First I suspect it's caused by file permission, but the issue remain the same even if I allowed everyone to be able to read/write.
I have been stucked with this problems for many hours and couldn't find a proper solution. Can anyone shed some light on this? Thanks!
Databases in the bundle are read only. If the file doesn't exist at the destination folder where you define, you should copy it from the bundle to the library or documents folder and then connect to that. That means it will copy on first use of that path.
Here's a function to 'prepare' the database by copying it to the destination from the bundle. It copies it to library (from my iOS app) but you can copy wherever you want. In my case, it was contacts.db.
I called this method from ensureOpened.
- (BOOL)ensureDatabasePrepared: (NSError **)error
{
// already prepared
if ((_dbPath != nil) &&
([[NSFileManager defaultManager] fileExistsAtPath:_dbPath]))
{
return YES;
}
// db in main bundle - cant edit. copy to library if !exist
NSString *dbTemplatePath = [[NSBundle mainBundle] pathForResource:#"contacts" ofType:#"db"];
NSLog(#"%#", dbTemplatePath);
NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
_dbPath = [libraryPath stringByAppendingPathComponent:#"contacts.db"];
NSLog(#"dbPath: %#", _dbPath);
// copy db from template to library
if (![[NSFileManager defaultManager] fileExistsAtPath:_dbPath])
{
NSLog(#"db not exists");
NSError *error = nil;
if (![[NSFileManager defaultManager] copyItemAtPath:dbTemplatePath toPath:_dbPath error:&error])
{
return NO;
}
NSLog(#"copied");
}
return YES;
}

Failure to write NSString to file on iPad

I'm using a text file to save the changes made by a user on a list (the reason that I'm doing this is so that I can upload the text file to a PC later on, and from there insert it into an Excel spreadsheet). I have 3 data structures: A NSMutableArray of keys, and a NSMutableDictionary who's key values are MSMutableArrays of NSStrings.
I iterate through these data structures and compile a file string that looks much like this:
(Key);(value)\t(value)\t(value):\n(Key);(value).. .so on.
SO, onto the actual question: When I attempt to save it, it fails. I'm 99% sure this is because of the file path that I'm using, but I wanted backup to check this out. Code follows:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *filePath = [paths objectAtIndex:0];
NSString *fileString = [NSString stringWithString:[self toFileString]];
if(![fileString writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:NULL]){
NSLog(#"File save failed");
} else {
// do stuff
}
(Code above is re-copied, since the actual code is on a different computer. It compiles, so ignore spelling errors?)
I tried using NSError, but I got bogged down in documentation and figured I might as well ask SO while trying to figure out how to properly use NSError (might be a little bit of an idiot, sorry).
99% sure it's the NSArray *paths line that's tripping it up, but I don't know how else to get the documents directory.
Edit: Problem solved, and one final question: If I save it to the App's document directory, where can I go after I close the app to see if it saved properly? If it works like I think it does, isn't it sandboxed in with the app's installation on the simulator? (i.e. no way of checking it)
NSLog() that filePath string. I think you're trying to write to the directory itself, not to a file.
Try this instead:
filePath = [[paths objectAtIndex:0]stringByAppendingPathComponent:#"myfile.txt"];
What is the file name you want to save? The method
NSArray *paths = NSSearchPathForDirectoriesInDomains(...);
NSString *filePath = [paths objectAtIndex:0];
...
if(![fileString writeToFile:filePath ...
means you are saving the string into a file path which has the same name as a folder. This will of course fail. Please give it a name, e.g.
NSString* fileName = [filePath stringByAppendingPathComponent:#"file.txt"];
if(![fileString writeToFile:fileName ...
and try again.
BTW, to use NSError:
NSError* theError = nil;
if(![fileString writeToFile:fileName ... error:&theError]) {
// ^^^^^^^^^
NSLog(#"Failed with reason %#", theError);
// theError is autoreleased.
}