Array of NSStrings from filenames within a folder? - iphone

I'm trying to create an array of NSStrings of the contents of a folder that I've dragged into my project... but when I count the items in the array afterwards, it's always comes back with 0;
So, my folder in my project looks like this
-Cards
-Colors
Blue.png
Green.png
Orange.png
Yellow.png
Purple.png
Black.png
And my code which tries to get this list of files (the color pngs) is
NSError *error = nil;
NSString *pathString = [[NSString alloc] init];
pathString = [[NSString alloc] initWithString:#"/Cards/Colors/"];
NSArray *fileList = [[NSArray alloc] init];
fileList = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:pathString error: &error];
[pathString release];
NSLog(#"%#", error);
// this is always 0
NSLog(#"file list has %i items", [fileList count]);
The NSError I get is
Error Domain=NSCocoaErrorDomain Code=260 "The operation couldn’t be completed. (Cocoa error 260.)" UserInfo=0x596db00 {NSUserStringVariant=(
Folder
), NSFilePath=/Cards/Color/, NSUnderlyingError=0x5925ef0 "The operation couldn’t be completed. No such file or directory"}
Any ideads where I am going wrong?

You're initializing pathString to the absolute path /Cards/Colors/. This path is a system-wide path, so on the iPhone, far outside your app's sandbox.
Try this instead:
NSString *pathString = [[NSBundle mainBundle] pathForResource:#"Cards/Colors" ofType:nil];
NSArray *fileList = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:pathString error: &error];
(Note that the way you have your code in the question, you alloc/init fileList, then immediately leak the object by assigning to it the results of contentsOfDirectoryAtPath:error:. This is a bug.)

Related

ios not finding a txt file using stringWithContentsOfFile

I have a text file thetext.txt. Which is in my project and is copied on build, in build settings. In the same way that my GL shaders and textures are (which work fine.)
NSError *errorReading;
NSArray *linesOfText = [[NSString stringWithContentsOfFile:#"thetext.txt"
encoding:NSUTF8StringEncoding
error:&errorReading]
componentsSeparatedByString:#"\n"];
NSLog(#"Reading error %#",errorReading);
It prints the following to console.
Reading error Error Domain=NSCocoaErrorDomain Code=260 "The operation couldn’t be completed. (Cocoa error 260.)" UserInfo=0x896fff0 {NSFilePath=thetext.txt, NSUnderlyingError=0x896ff60 "The operation couldn’t be completed. No such file or directory"}
Have I missing something?
This fails because you are passing the file name and not the path to the file. Try something like this
NSString* filePath = [[NSBundle mainBundle] pathForResource:#"thetext" ofType:#"txt"];
NSError *errorReading;
NSArray *linesOfText = [[NSString stringWithContentsOfFile:filePath
encoding:NSUTF8StringEncoding
error:&errorReading]
componentsSeparatedByString:#"\n"];
NSLog(#"Reading error %#",errorReading);
Hopefully there will be no error!

replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error: broken in iOS 6?

I cannot get the NSFileManager method replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error: to work in iOS 6. Apps that call this method and worked fine on iOS 5 have major issues on iOS 6. The problem does not occur on devices running versions of iOS below 6.0. The problem does not occur if the app is launched in the iOS Simulator by Xcode. Otherwise the problem seems to be universal.
Here is the test code I am trying to execute:
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *destinationPath = [documentsDirectory stringByAppendingPathComponent:#"test.txt"];
NSString *sourcePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"test.txt"];
// Create initial file in documents directory
if (![fileManager fileExistsAtPath:destinationPath])
{
BOOL fileCopied = [fileManager copyItemAtPath:sourcePath
toPath:destinationPath
error:&error];
if (!fileCopied)
[[self statusLabel] setText:[NSString stringWithFormat:#"Creation Error:\n\n%#",
[error localizedDescription]]];
}
// Replace file in documents directory with copy of file from app bundle
if ([fileManager fileExistsAtPath:destinationPath])
{
NSURL *destinationURL = [NSURL fileURLWithPath:destinationPath];
BOOL fileReplaced = [fileManager replaceItemAtURL:destinationURL
withItemAtURL:[NSURL fileURLWithPath:sourcePath]
backupItemName:nil
options:0
resultingItemURL:&destinationURL
error:&error];
if (!fileReplaced)
[[self statusLabel] setText:[NSString stringWithFormat:#"Replacement Error:\n\n%#",
[error localizedDescription]]];
else
[[self statusLabel] setText:#"Successfully replaced file."];
}
It creates the file in the documents directory, if it doesn’t already exist. It then attempts to replace the file in the documents directory with a copy of a file from the app bundle. It then reports the status of the file creation/replacement. As I said before, it replaces fine if it’s being run on iOS 5 or lower or if it’s being run in the iOS Simulator with Xcode attached to the process. However, if it’s run on an iOS 6 device or the iOS Simulator without Xcode the replacement fails and an error is returned. The localized error description is The operation couldn’t be completed. (Cocoa error 512.).
The user info dictionary for the error is:
{
NSFileNewItemLocationKey = "file://localhost/var/mobile/Applications/487FBB9E-A2BD-4CF2-BB38-F36764623C2F/Test.app/test.txt";
NSFileOriginalItemLocationKey = "file://localhost/var/mobile/Applications/487FBB9E-A2BD-4CF2-BB38-F36764623C2F/Documents/test.txt";
NSURL = "file://localhost/var/mobile/Applications/487FBB9E-A2BD-4CF2-BB38-F36764623C2F/Documents/test.txt";
NSUnderlyingError = "Error Domain=NSCocoaErrorDomain Code=513 \"The operation couldn\U2019t be completed. (Cocoa error 513.)\" UserInfo=0x1d58d350 {NSFilePath=/var/mobile/Applications/487FBB9E-A2BD-4CF2-BB38-F36764623C2F/Test.app/test.txt, NSURLUnsetValueKeysKey=<CFArray 0x1d58d180 [0x39b9d100]>{type = immutable, count = 2, values = (\n\t0 : <CFString 0x39b945b4 [0x39b9d100]>{contents = \"NSURLFileSecurityKey\"}\n\t1 : <CFString 0x39b943d4 [0x39b9d100]>{contents = \"NSURLCreationDateKey\"}\n)}, NSUnderlyingError=0x1d58d010 \"The operation couldn\U2019t be completed. Operation not permitted\", NSURL=file://localhost/var/mobile/Applications/487FBB9E-A2BD-4CF2-BB38-F36764623C2F/Test.app/test.txt}";
}
I have an app on the App Store which depends on this method. The live app continues to work without flaw on iOS 5, but on iOS 6 it is has huge problems due to the method failure. Does anyone know why this method is failing?
The NSFileManager method replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error: is not a copy method; it is a move method. I.e., the file isn’t replaced with a copy of the replacement file, but with the replacement file itself. Since an app is not supposed to be able to modify its own bundle, the above code should never have worked in any version of iOS.
To retain atomicity, the solution is to first save a copy of the replacement file to the temporary directory, then replace the file with the copy in the temporary directory.
Here is the fixed test code:
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *sourcePath = [[NSBundle mainBundle] pathForResource:#"test" ofType:#"txt"];
NSString *destinationPath = [documentsDirectory stringByAppendingPathComponent:#"test.txt"];
// Create initial file in documents directory
if (![fileManager fileExistsAtPath:destinationPath])
{
BOOL fileCopied = [fileManager copyItemAtPath:sourcePath
toPath:destinationPath
error:&error];
if (!fileCopied)
[[self statusLabel] setText:[NSString stringWithFormat:#"Creation Error:\n\n%#", [error localizedDescription]]];
}
// Replace file in documents directory with copy of file from app bundle
if ([fileManager fileExistsAtPath:destinationPath])
{
// Create temporary file
NSString *tempPath = [NSTemporaryDirectory() stringByAppendingPathComponent:#"test.txt"];
BOOL tempCopied = [fileManager copyItemAtPath:sourcePath
toPath:tempPath
error:&error];
if (!tempCopied)
[[self statusLabel] setText:[NSString stringWithFormat:#"Temp Creation Error:\n\n%#", [error localizedDescription]]];
// Replace file with temporary file
NSURL *destinationURL = [NSURL fileURLWithPath:destinationPath];
BOOL fileReplaced = [fileManager replaceItemAtURL:destinationURL
withItemAtURL:[NSURL fileURLWithPath:tempPath]
backupItemName:nil
options:0
resultingItemURL:&destinationURL
error:&error];
if (!fileReplaced)
[[self statusLabel] setText:[NSString stringWithFormat:#"Replacement Error:\n\n%#", [error localizedDescription]]];
else
[[self statusLabel] setText:#"Successfully replaced file."];
}

"The operation couldn’t be completed. (Cocoa error 512.)"

I have this code, which should be working perfectly, but I can't udnerstand why it isn't:
+(NSString *)writeImageToFile:(UIImage *)image {
NSData *fullImageData = UIImageJPEGRepresentation(image, 1.0f);
NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/Images/"];
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL isDirectory = NO;
BOOL directoryExists = [fileManager fileExistsAtPath:path isDirectory:&isDirectory];
if (directoryExists) {
NSLog(#"isDirectory: %d", isDirectory);
} else {
NSError *error = nil;
BOOL success = [fileManager createDirectoryAtPath:path withIntermediateDirectories:NO attributes:nil error:&error];
if (!success) {
NSLog(#"Failed to create directory with error: %#", [error description]);
}
}
NSString *name = [NSString stringWithFormat:#"%#.jpg", [JEntry generateUuidString]];
NSString *filePath = [path stringByAppendingPathComponent:name];
NSError *error = nil;
BOOL success = [fullImageData writeToFile:filePath options:NSDataWritingAtomic error:&error];
if (!success) {
NSLog(#"Failed to write to file with error: %#", [error description]);
}
return filePath;
}
It passed the directoryExists without an error, but when it gets to writeToFile, it gives me this error:
Error Domain=NSCocoaErrorDomain Code=512 "The operation couldn’t be completed. (Cocoa error 512.)" UserInfo=0x5634ee0 {NSFilePath=/var/mobile/Applications/5E25F369-9E05-4345-A0A2-381EDB3321B8/Documents/Images/18DAE0BD-6CB4-4244-8ED1-9031393F6DAC.jpg, NSUnderlyingError=0x5625010 "The operation couldn’t be completed. Not a directory"}
Any ideas why this might be?
I was able to reproduce your error when writing a file first in the path #"Documents/Images/", then trying to write the image using your code.
I think there are two possible scenarios for this:
1) You created that file by mistake at a previous execution of your app. This will be solved if you reset the simulator using the menu: iOS Simulator > Reset Contents and Settings, and uninstalling the app from your device: Long press > click on the x symbol.
2) There is some code somewhere else in your app that creates this file. If this is the case, you should find this code and remove it.
From FoundationErrors.h:
NSFileWriteUnknownError = 512
Try using withIntermediateDirectories:YES.
In my case a period '.' in the directory name (e.g. ~/Documents/someDir.dir/somefile) was the cause of the problem. I removed the offending character and the error disappeared.

Copying file from bundle to documents not working (Cocoa error 512)

I am trying to simply copy my sqlite3 database to the documents directory. I get a Cocoa Error 512 and what I figured about that is, that it's not a valid directory (or something like that.
The database file is in my Resources folder in XCode. (The name of file is correct)
Here is the code I am trying to use:
-(void) checkAndCreateDatabase
{
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
//databasePath + databaseName is declared in the header
databaseName = #"WaypointDatabase.sql";
databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
success = [fileManager fileExistsAtPath:databasePath];
if(success)
{
NSLog(#"Database exists");
return;
}
else
NSLog(#"Database does not exists");
NSString *databasePathFromApp = [[NSBundle mainBundle] pathForResource:#"WaypointDatabase" ofType:#"sql"];
if(databasePathFromApp == nil)
{
NSLog(#"ERROR: IT IS NIL");
}
NSError* error = nil;
NSLog(#"Path in bundle:\n%#\n\n", databasePathFromApp);
NSLog(#"Path to copy to:\n%#\n\n", databasePath);
[fileManager copyItemAtPath:databasePathFromApp
toPath:databasePath error:&error];
[fileManager release];
if (error)
{
NSLog(#"%#\n\n", error);
NSLog(#"%#", [error userInfo]);
}
}
And the output to console that I get is:
2011-10-30 10:36:13.242 xxxx[6726:707] Database does not exists
2011-10-30 10:36:13.249 xxxx[6726:707] Path in bundle:
/var/mobile/Applications/FF654016-4257-47BB-99FE-55DB5453BBC6/xxxx.app/WaypointDatabase.sql
2011-10-30 10:36:13.252 xxxx[6726:707] Path to copy to:
/var/mobile/Applications/FF654016-4257-47BB-99FE-55DB5453BBC6/Documents/WaypointDatabase.sql
2011-10-30 10:36:13.268 xxxx[6726:707] Error Domain=NSCocoaErrorDomain Code=512 "The operation couldn’t be completed. (Cocoa error 512.)" UserInfo=0xe8b7cd0 {NSUserStringVariant=(
Copy
), NSFilePath=/var/mobile/Applications/FF654016-4257-47BB-99FE-55DB5453BBC6/xxxx.app/WaypointDatabase.sql, NSDestinationFilePath=/var/mobile/Applications/FF654016-4257-47BB-99FE-55DB5453BBC6/Documents/WaypointDatabase.sql, NSUnderlyingError=0xe8b7ee0 "The operation couldn’t be completed. Not a directory"}
2011-10-30 10:36:13.272 xxxx[6726:707] {
NSDestinationFilePath = "/var/mobile/Applications/FF654016-4257-47BB-99FE-55DB5453BBC6/Documents/WaypointDatabase.sql";
NSFilePath = "/var/mobile/Applications/FF654016-4257-47BB-99FE-55DB5453BBC6/xxxx.app/WaypointDatabase.sql";
NSUnderlyingError = "Error Domain=NSPOSIXErrorDomain Code=20 \"The operation couldn\U2019t be completed. Not a directory\"";
NSUserStringVariant = (
Copy
);
}
I figure this must be to do with getting the directory paths or something, but have been stuck on this for a few days now.
Take note, this works perfectly & without any errors on the simulator.
Where could I have gone wrong?
[UPDATE]
Could this be because the Documents-folder directory does not exist? How would I go about to create/check it?
[UPDATE 2]
I have done some other tests using
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *err = nil;
NSArray *dirArr = [fileManager contentsOfDirectoryAtPath:documentsDir error:&err];
NSLog(#"~~Contents:\n%#",dirArr);
NSLog(#"~~Error: \n%#",err);
And surprisingly it gave Cocoa Error 256 and also said "Not a directory". It is almost as if the Documents directory does not exist. But it does, according toNSSearchPathForDirectoriesInDomains`
Here is the output I got
2011-10-30 10:59:16.934 xxxx[6774:707] ~~Contents:
(null)
2011-10-30 10:59:16.936 xxxx[6774:707] ~~Error:
Error Domain=NSCocoaErrorDomain Code=256 "The operation couldn’t be completed. (Cocoa error 256.)" UserInfo=0x10052580 {NSUserStringVariant=(
Folder
), NSFilePath=/var/mobile/Applications/FF654016-4257-47BB-99FE-55DB5453BBC6/Documents, NSUnderlyingError=0x10054a50 "The operation couldn’t be completed. Not a directory"}
The problem got solved by setting a non standard Bundle ID in die info.plist
I used the Bundle ID from iTunes Connect for this specific app. Now everything works perfectly.
Try changing
[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:databaseName];
for
[[NSBundle mainBundle] pathForResource:#"WaypointDatabase" ofType:#"sql"];
and check that this does not return nil to ensure that the is not what's going wrong apart from that it looks fine to me on first skim.

How to move a directory to a new location on iPhone

Here's my method which should simply move the contents of a directory from /someDirectory to /addons/id/UUID:
CFUUIDRef uuidObj = CFUUIDCreate(nil); //create a new UUID
//get the string representation of the UUID
NSString *uuidString = (NSString*)CFUUIDCreateString(nil, uuidObj);
//MOVE the addon to the addons directory addons/shortname/UUID
NSString *pathToAddon = [LWEFile createDocumentPathWithFilename:[NSString stringWithFormat:#"%#", relPath]];
NSString *pathToAddonDest = [LWEFile createDocumentPathWithFilename:[NSString stringWithFormat:#"addons/%#/%#", [character objectForKey:#"shortName"], uuidString]];
// move the files
NSError* error;
if([[NSFileManager defaultManager] moveItemAtPath:pathToAddon toPath:pathToAddonDest error:&error] != YES)
{
NSLog(#"Unable to move file: %#", [error localizedDescription]);
}
//release the uuid stuff
[uuidString release];
CFRelease(uuidObj);
The move fails with The operation couldn’t be completed. (Cocoa error 4.). However the same code works if I change pathToAddonDest to:
NSString *pathToAddonDest = [LWEFile createDocumentPathWithFilename:[NSString stringWithFormat:#"addons/%#", [character objectForKey:#"shortName"], uuidString]];
So I can write from /someDirectory to /addons/someDirectory but not from /someDirectory to /addons/someDirectory/UUID.
Any ideas why a seemingly simple rename wouldn't work in this way?
You should create directory before before you could move it there.
/addons/someDirectory/UUID --- create this path before you try moving the content.