NSFileManager - URLsForDirectory... or URLForDirectory - ios

-[NSFileManager URLForDirectory:inDomain:appropriateForURL:create:error:] requires a single NSSearchPathDomainMask and returns a single URL. (The ...appropriateForURL:create:error: part is a bit confusing in documentation.)
-[NSFileManager URLsForDirectory:inDomains:] allows you to create a bit-mask for the domains parameter and returns an array of URLs.
It seems to me there is overlap between these two methods. If my goal is to get the Documents, or Library, or etc directory from an iOS app's sandbox, when should I use one over the other?

The standard way to get access to the Documents directory (or other similar directories) is code like the following:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = paths[0];
This is similar to doing:
NSArray *URLs = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
NSURL *documentsURL = URLs[0];
The key difference is the first gives you the path as an NSString while the second gives you the path as an NSURL.
The other method can be used by doing:
NSURL *documentsURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
You can pass NO for the Documents directory because it always exists. You should pass YES for the application support directory since it doesn't exist by default. And ideally you should not pass in nil for the error so you can see what happened if the method call returns nil.
Any of these three approaches work. Use the 1st if you want the path as a string. Use the 3rd if you want it as a URL. Use the 2nd if you have the rare need to pass in more than one domain.

Related

How should an iOS library handle logging to a file on iPhone's file system?

I am working on an iOS library project and need to create an API that takes an NSString parameter which is a path, and the library will write some debugging messages to a file in that path.
I've done some research about logging onto a file in iPhone's file system, one approach is using
freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding],"a+",stderr);
This will redirect any following NSLog to a file...
While this seems easy, I have a question: Will this also redirect the API consumer's application(calling application)'s NSLog to the file? I don't want this behavior because I want to be able to control what goes in there as a library..
If that is the behavior, what other approach I can use to achieve my requirement? Thanks.
If I understood correctly, the desired functionality is to pass a path and write some debugging info to a file on that path? If that is so, I don't think you should redirect all your NSLog calls to a file; just using the NSString writeToFile: would be enough:
-(void)writeToFile:(NSString*)path {
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *fileName = [documentsDirectory stringByAppendingPathComponent:path];
NSError *error=nil;
NSString *myMessage="This is the data to write to the file";
[myMessage writeToFile:fileName atomically:YES encoding:NSUTF8StringEncoding error:&error];
}
If you want to append to that file, you can use the NSFileHandle functionality - check the docs here.

iOS - Iteration in some folders

I need to get some files in definite folders. For this purpose I use this answer and it works fine. However, my folder structure is like:
Documents/
idOfFolder1/
images/
image1.jpg
image2.png
sounds/
sound1.mp3
files/
file1.pdf
idOfFolder2/
images/
...
I need to iterate through all folders having a specific id and get images. Since the answer written iterates through all folders it takes much more time than normal (we have many files). To optimize process, I want to skip "sounds" and "files" folders. Is there a way to handle this?
Thank you!
If you don't like to use NSPredicate, following code will also do what you want,
(this code will directly access the folders with given ids and pick everything inside the images folders)
-(void) printImagesOfFolders:(NSArray *) folderIds{
for(NSString *folderId in folderIds){
NSArray *paths = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
NSURL *documentsURL = [paths lastObject];
NSString * imageFolderPath = [NSString stringWithFormat:#"%#/images", folderId];
NSURL *rootURL = [documentsURL URLByAppendingPathComponent:imageFolderPath];
NSFileManager *fm = [NSFileManager defaultManager];
NSDirectoryEnumerator *dirEnumerator = [fm enumeratorAtURL:rootURL
includingPropertiesForKeys:#[NSURLNameKey, NSURLIsDirectoryKey]
options:NSDirectoryEnumerationSkipsHiddenFiles
errorHandler:nil];
for (NSURL *url in dirEnumerator) {
NSLog(#"url: %#", url);
}
}
}
How to use it
[self printImagesOfFolders:#[#"idOfFolder1",#"idOfFolder2",#"idOfFolder3",#"idOfFolder4"]];
I think you can implement it in a such different way using a NSPredicate onto the NSDirectoryEnumerator array as in this answer Iterating through files in nested folders or Filter NSFileManager with NSPredicate but the syntax can be awkward: take a look at NSPredicate syntax documentation NSPredicate Format String
In your case I would either:
1. use fast enumeration with a for loop
2. if possible redesign the folder structures
3. use core data or another caching system to speed up the loading process (this involves to sync time to time with the real files)
That's right. The upstairs is right, and the index will be named directly based on the picture so that CPU can find the files accurately

Can app have multiple paths for NSDocumentDirectory

In any scenario can app have multiple paths in NSArray when accessing NSDocumentDirectory using following code :
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
I am just curious about thing..can you all share your experience with me?
Strictly spoken you can get multiple paths because you can pass multiple domains.
If you pass a single domain you get an array containing a single path.
However Apple recommends not to use this function anymore.
From the documentation:
You should consider using the NSFileManager methods URLsForDirectory:inDomains: and URLForDirectory:inDomain:appropriateForURL:create:error: which return URLs, which are the preferred format.
No this will always return one path for documentDirectory!!
It may vary as per different kind of directory like,
NSString *path = NSTemporaryDirectory();
above line will give temp directory's path.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES);
above array have also one path but this is path of NSDocumentationDirectory.
NSSearchPathForDirectoriesInDomains Creates a list of path strings for the specified directories in the specified domains. The list is in the order in which you should search the directories.
You can get content or sub directory of document directory or any directory like,
NSArray *pathArr = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSAllDomainsMask, YES);
NSArray *contentOfDocDirectory = [[NSFileManager defaultManager]contentsOfDirectoryAtPath:pathArr[0] error:nil];
NSLog(#"content of document directory : %#",contentOfDocDirectory);

NSFileManager UUID app directory

I'm using NSFileManager and i'm trying to copy a file to path "/var/mobile/Applications/7AC2295E-2775-41EA-B017-AB4048A09F0C/Document" the file will copy fine.
but the path of "7AC2295E-2775-41EA-B017-AB4048A09F0C" is randomly changed in every time i delete and install the app again. So, is there a way to get the correct path of my app or search for file name, If file exist then replace\delete..etc the file? thanks alot.
The proper way to get access to the Documents directory in your app's sandbox is:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = paths[0];
Then you create your path:
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"filename.ext"];
You don't have to use the absolute path, use something like
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0];
[[NSFileManager defaultManager] copyItemAtPath:[[NSBundle mainBundle]pathForResource:#"test" ofType:#"txt"] toPath:[documentsDirectory stringByAppendingPathComponent:#"test.txt"] error:nil];
This demonstrates copying the file test.txt from the apps bundle to the documents directory.
Do something like this to create a file test.txt
[[NSFileManager defaultManager] createFileAtPath:[documentsDirectory stringByAppendingPathComponent:#"test.txt"] contents:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://a-cstudios.com/text.json"]] attributes:nil];
EDIT
If your using this and want to use it on a jailbroken device, to put something in the sandbox is the same as I said before, so you still don't have to use the UUID. If you want to put something in a system directory, that is when you would use something like /usr/include/, or wherever you want to. To put something in another apps sandbox, you would have to use the complete path, and you would have to know the UUID, as thats not part of your sandbox. So, unless you're copying a file to an app that you made, you have to use the complete path.
If your application needs to write a file to its Documents directory.
use something like :
NSString *filePath = NSHomeDirectory();
then append your actual file name to filePath.
then use filePath in your FileManager calls.

Xcode IOS How To Access Files With Unknown Filename

My app allows users to create and delete files from the documents folder within the app. They can do it through the app itself or manually using itunes. Is there are way to access what's left in the documents folder without any hardcoding of the filenames?
Thank you.
You can get NSArray with all items in given directory using contentsOfDirectoryAtPath:error: method in NSFileMamanger:
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSArray *allItems = [[NSFileManager sharedFileManager] contentsOfDirectoryAtPath:documentsDirectory error:NULL];

Resources