fileExistsAtPath returns NO for a directory that exists - objective-c

fileExistsAtPath is returning NO for a directory that exists. If I have the following code:
NSURL *newDirectoryPath = ... // path does not begin with a tilda
// and is created using URLByAppendingPathComponent:isDirectory:
BOOL directoryExists = [self.fileManager fileExistsAtPath:[newDirectoryPath absoluteString]];
if (NO == directoryExists)
{
BOOL ret = [self.fileManager moveItemAtURL:self.currentPresentationDirectoryPath toURL:newDirectoryPath error: &err];
// ret is YES, err is nil
directoryExists = [self.fileManager fileExistsAtPath:[newDirectoryPath absoluteString]];
}
Even though the directory has just been created successfully with moveItemAtURL, fileExistsAtPath is still returning NO.
I know the documentation says this:
Attempting to predicate behavior based on the current state of the
file system or a particular file on the file system is not
recommended. Doing so can cause odd behavior in the case of file
system race conditions.
But I want to understand what the issue is here - if I close the app and relaunch it then the first check for fileExistsAtPath in the code above is still returning NO, even though the directory was previously successfully created during the prior execution of the code, and I can see the directory in the Organizer, and I can also successfully read from the contents of the directory etc. etc.
P.S. is there no fileExistsAtURL: method?

If you have an NSURL, -absoluteURL won't return a usable path for NSFileManager. It will return the absolute URL with the file:// prefix. E.g.: file:///path/to/file.
Instead try to use an other method, like -path. Check if that works.
NSURL *myURL = /* some url */;
NSString *myPath;
BOOL exi;
myPath = [myURL path];
exi = [[NSFileManager defaultManager] fileExistsAtPath:myPath];
if(!exi) {
NSLog(#"File does not exist");
}

Related

Why can't NSFIleManager -fileExistsAtPath not find an existing file when path is correct?

I know that the iOS Simulator is found in a different directory each time it is run; with that in mind, I have this code which gives me the directory of the Core Data sqlite files:
// find current directory for saori.sqlite
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *documentDirectory = [[fileManager URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]firstObject];
NSString *sqliteFilePath = [[documentDirectory URLByAppendingPathComponent:#"Application Support/SalonBook/saori.sqlite"] absoluteString];
if ([fileManager fileExistsAtPath:sqliteFilePath])
[MagicalRecord cleanUp]; // set stack, etc to 'nil'
else {
NSLog(#"\n\n-->sqlite files not found"); // log message "unable to find sqlite files
return;
}
This is the printout of the sqliteFilePath object:
Printing description of sqliteFilePath:
file:///Users/rolfmarsh/Library/Developer/CoreSimulator/Devices/1EE69744-255A-45CD-88F1-63FEAD117B32/data/Containers/Data/Application/C8FF20F0-41E4-4F26-AB06-1F29936C2208/Library/Application%20Support/SalonBook/saori.sqlite
And this is the image of the file from Finder:
The problem is: I go to the sqliteFilePath and the saori.sqlite file is indeed there! Why is -fileExistsAtPath failing?
Because it is still a URL. A file path doesn't have a protocol, so the prefix of your path file:/// is invalid and can't be resolved. Since an invalid path doesn't contain any files, fileExistsAtPath: returns NO.
Not to worry though, instead of calling absoluteString on the URL object, you can just call path instead and it will return the path.

OSX application loses permissions to read a directory after app re-start

My OSX application is intended to monitor a folder on the computer which the user has selected. Very simply, I have a function which scans the directory:
- (NSError*)scan:(NSString*)dir {
NSError *err = nil;
NSArray *filenames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dir error:&err];
for(NSString* fn in filenames) {
NSString *fp = [dir stringByAppendingPathComponent:fn];
BOOL isDir;
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:fp isDirectory:&isDir];
if(exists && isDir) {
[self scan:fp];
}
else {
[self handleFile:fp];
}
}
NSLog(#"Scanning %#: %#",dir,err);
return err;
}
When I first start the application, I present a UI to select the folder and then immediately scan it. The operation works great (no errors).
When I restart the application, I attempt to scan the directory again, but this time the NSLog spits out an error:
Scanning /Users/zane/Dropbox/Gifs: Error Domain=NSCocoaErrorDomain Code=257 "The file “Gifs” couldn’t be opened because you don’t have permission to view it." UserInfo=0x608000476d40 {NSFilePath=/Users/zane/Dropbox/Gifs, NSUserStringVariant=(
Folder
), NSUnderlyingError=0x60800005d3d0 "The operation couldn’t be completed. (OSStatus error -5000.)"}
Note that the path has not changed.
I suspect that the problem has to do with .entitlements. Here's what I've got:
If you are going to use the same file or folder the user already selected, then you need to save the path as a security-scoped bookmark when the user selects that path. Read its bookmark data when the user restarts the application in order to use the saved path. There's a description of security-scoped bookmark at the middle of this page. Use the URLByResolvingBookmarkData method of NSURL to return a security-scoped bookmark. Use the bookmarkDataWithOptions method or equivalent of NSURL to resume the security-scoped bookmark.

Confused on creating new directories and changing directories (Objective-C)

When I changed the current directory path of main.m to newDir why does it still say that there are no files in newDir? Also I ran this program multiple times with no errors. Does that mean I ended up creating multiple newDir?
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
#autoreleasepool {
NSString *newDir = #"newDir";
NSFileManager *manager = [NSFileManager defaultManager];
if ([manager createDirectoryAtPath:newDir withIntermediateDirectories:YES attributes:nil error:NULL] == NO) {
NSLog(#"couldnt create new directory");
return 1;
}
if ([manager changeCurrentDirectoryPath: newDir] == NO) {
NSLog(#"couldnt change directory path");
return 2;
}
NSLog(#"%#", [manager currentDirectoryPath]);
NSLog(#"%#", [manager contentsOfDirectoryAtPath:newDir error:NULL]);
}
return 0;
}
Output:
2012-08-07 10:27:20.428 Test[853:707] /Users/ss/Library/Developer/Xcode/DerivedData/Test-bfrqtnrhaafmdzghoyirjnfqjbfc/Build/Products/Debug/newDir
2012-08-07 10:36:47.832 Test[885:707] (null)
The path to main.m does not play into what happens when you run your program: the only question is whether the directory has any files or not, and from the log it appears that it doesn't.
To create some files in the directory, run these commands in the terminal window:
touch /Users/ss/Library/Developer/Xcode/DerivedData/Test-bfrqtnrhaafmdzghoyirjnfqjbfc/Build/Products/Debug/newDir/quick.txt
touch /Users/ss/Library/Developer/Xcode/DerivedData/Test-bfrqtnrhaafmdzghoyirjnfqjbfc/Build/Products/Debug/newDir/brown.txt
touch /Users/ss/Library/Developer/Xcode/DerivedData/Test-bfrqtnrhaafmdzghoyirjnfqjbfc/Build/Products/Debug/newDir/fox.txt
This will create three empty files. Now run your program, and see if it discovers the newly created txt files; it should.
On your second question, the operating system would not let you create multiple file system objects with identical names, so the answer is no, you created only one newDir.
Since you're ignoring errors on your contentsOfDirectoryAtPath call, it's entirely possible that the call is failing -- hence the null.
Without looking at references, it appears that you're looking for directory .../newDir/newDir, a directory that likely does not exist.
In any event, since newDir is new it wouldn't contain any entries (other than . and ..).

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
}

How to check if a folder exists in Cocoa & Objective-C?

How to check if a folder (directory) exists in Cocoa using Objective-C?
Use NSFileManager's fileExistsAtPath:isDirectory: method. See Apple's docs here.
Some good advice from Apple in the NSFileManager.h regarding checking the file system:
"It's far better to attempt an operation (like loading a file or creating a directory) and handle the error gracefully than it is to try to figure out ahead of time whether the operation will succeed. Attempting to predicate behavior based on the current state of the filesystem or a particular file on the filesystem is encouraging odd behavior in the face of filesystem race conditions."
[NSFileManager fileExistsAtPath:isDirectory:]
Returns a Boolean value that indicates whether a specified file exists.
- (BOOL)fileExistsAtPath:(NSString *)path isDirectory:(BOOL *)isDirectory
Parameters
path
The path of a file or directory. If path begins with a tilde (~), it must first be expanded with stringByExpandingTildeInPath, or this method will return NO.
isDirectory
Upon return, contains YES if path is a directory or if the final path element is a symbolic link that points to a directory, otherwise contains NO. If path doesn’t exist, the return value is undefined. Pass NULL if you do not need this information.
Return Value
YES if there is a file or directory at path, otherwise NO. If path specifies a symbolic link, this method traverses the link and returns YES or NO based on the existence of the file or directory at the link destination.
NSFileManager is the best place to look for file related APIs. The specific API you require is
- fileExistsAtPath:isDirectory:.
Example:
NSString *pathToFile = #"...";
BOOL isDir = NO;
BOOL isFile = [[NSFileManager defaultManager] fileExistsAtPath:pathToFile isDirectory:&isDir];
if(isFile)
{
//it is a file, process it here how ever you like, check isDir to see if its a directory
}
else
{
//not a file, this is an error, handle it!
}
If your have a NSURL object as path, it's better to use path to convert it into NSString.
NSFileManager*fm = [NSFileManager defaultManager];
NSURL* path = [[[fm URLsForDirectory:NSDocumentDirectory
inDomains:NSUserDomainMask] objectAtIndex:0]
URLByAppendingPathComponent:#"photos"];
NSError *theError = nil;
if(![fm fileExistsAtPath:[path path]]){
NSLog(#"dir doesn't exists");
}else
NSLog(#"dir exists");