in Objective C how would I get a list of ALL files on the iPhone?
is this even possible or can I only get files from a certain directory or known path?
Not without jail-breaking your phone. All apps live in a sandbox and can only see certain files.
No you can just get files within your sandbox. And to display your current directory use the following code :
- (NSString *) returnFilePath {
NSArray *pathArray =
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [pathArray objectAtIndex:0];
}
I am not sure why people are saying that your app can only see certain files inside of your sandbox. As of iOS 2.x (the last time I tried something like this), you can use NSFileManager to list files in almost any directory. Here is a little code to get all of the names of all files in a specific directory.
- (NSArray *)allFiles:(NSString *)aPath
NSMutableArray * listing = [NSMutableArray array];
NSArray * fileNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:aPath error:nil];
if (!fileNames) return listing;
for (NSString * file in fileNames) {
NSString * absPath = [aPath stringByAppendingPathComponent:file];
BOOL isDir = NO;
if ([[NSFileManager defaultManager] fileExistsAtPath:absPath isDirectory:&isDir]) {
if (isDir) {
[listing addObject:absPath];
[listing addObjectsFromArray:[self allFiles:absPath]];
} else {
[listing addObject:absPath];
}
}
}
return listing;
}
This is an example of a very simple recursive function. It could, of course be modified to work more efficiently with blocks as callbacks or even incorporate NSOperation.
Related
In my iOS app I would like that user can download some jpg file via iTunes. So I've enabled UIFileSharingEnabled. But users are now able to put files in my app. I would like to block that. Is there a way to do that ?
Thanks !
Don't think you can block it, but you can just delete unwanted files when your app becomes active.
Put some code a bit like the sample below - filling in the test to avoid deleting the files you want to be available in iTunes.
Call this from within applicationDidBecomeActive: in your application delegate.
If you're more cautious you might want to check the user hasn't dropped a jpg file with the same name as the one you've parked there. You could test for sameness of date or some such or, if you've not got many files, just delete everything and write them again when the app becomes active.
- (void) removeUnwantedFiles;
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSArray* directoryContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:inboxPath error:NULL];
if (!directoryContents || [directoryContents count] == 0)
{
return;
}
for (NSString* fileName in directoryContents)
{
if ( /* some test of filename to see if it's one of my kosher files */ ) continue;
NSString* filePath = [documentsDirectory stringByAppendingPathComponent:fileName];
NSError* error = nil;
BOOL success = [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
// NSLog(#"Deleting (%#): %#", success ? #"succeeded" : #"failed", [filePath lastPathComponent]);
if (!success)
{
NSLog(#"Error: %#", [error localizedDescription]);
}
}
}
I am getting slightly frustrated with the DropBox API. It is supposed to be all simple and straight forward, but I have yet to come a across a simple and plain explanation of how to do a simple sync.
I followed all the instruction you can find in the readme which comes withe DropBox API. To test the whole thing, I have created two buttons to download and upload a file from or to my DropBox. The files are found in my app documents folder.
This works splendidly:
-(void) DBupload:(id)sender
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents directory
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"MyExample.txt"];
// NSError *error;
[self.restClient uploadFile:#"MyExample.txt" toPath:#"/MyExamplePath" fromPath:filePath];
}
-(void) DBdownload:(id)sender
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents directory
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"MyExample.txt"];
NSError *error;
[self.restClient loadFile:#"/myExamplePath/MyExample.txt" intoPath:filePath];
}
However, I am now wondering how to achieve a simply sync. Right now, I can manually upload and download. But what I need in order to sync is to:
find out if the MyExample.txt in my App's folder or in my DropBox folder is more recent
if the txt in the App's folder is more recent: drop it into dropbox (overriding the old), i.e. call my DBupload method
if the txt in the drop box is more recent: download it into the apps folder, i.e. call my DBdownload method
Perhaps I am just too dumb, but does dropBox detail somewhere how to achieve this rather simple and straight forward task?
I know that there is this but it doesn't really give any code samples.
Thanks for any suggestions.
EDIT
OK, so I figured that my first step is to find out the last modified date of MyExample.txt which is found in the dropBox.
I wrote a wonderful method called DBsync in which I simply put this command:
-(void) DBsync
{
[self.restClient loadMetadata:#"/myExamplePath"];
}
This calls the following method which gets the metadata. This was a suggested answer to this post, and I commented it a bit out so as to make it plain what is happening (if there are more dumb people like myself:
- (void)restClient:(DBRestClient*)client loadedMetadata:(DBMetadata*)metadata {
NSLog(#"restClient:loadedMetadata function called");
NSEnumerator *e= [metadata.contents objectEnumerator]; // indexes files in dropbox?
DBMetadata *dbObject; // loads metadate we need, e.g. lastModifiedDated
int numberOfFiles = [metadata.contents count]; // counts files in DropBox - I guess we don't really need this
NSLog(#"numberOfFiles %i", numberOfFiles);
while ((dbObject = [e nextObject])) { // this goes through every single file in the DropBox
if (!dbObject.isDirectory) { // this determines whether we are talking about a file or a folder
NSString *fileName = [dbObject.path lastPathComponent]; // this puts the name of the last component, e.g. in /MyExamplePath/MyExample.txt = MyExample.txt into fileName
NSLog(#"File which is currently being checked: %#", fileName);
if ([fileName isEqualToString:#"MyExample.txt"]) {
NSLog(#"Found it: %#", fileName);
NSLog(#"Date last modified: %#", dbObject.lastModifiedDate);
/* to do: call dbupload if dbObject.lastModifiedDate > than your local file*/
}
}
}
}
I will post the next step once I managed to do so...
I think what you are looking for is the loadmetadata method. Here is an untested example:
- (void)restClient:(DBRestClient*)client loadedMetadata:(DBMetadata*)metadata {
NSEnumerator *e= [metadata.contents objectEnumerator];
DBMetadata *dbObject;
numberOfFiles = [metadata.contents count];
while ((dbObject = [e nextObject])) {
if (!dbObject.isDirectory) {
NSString *fileName = [dbObject.path lastPathComponent];
if (![fileName isEqualToString:#"MyExample.txt"]) {
/* call dbupload if dbObject.lastModifiedDate > than your local file*/
}
}
}
You don't need an enumerator, just use the good old for... loop ;)
- (void)restClient:(DBRestClient *)client loadedMetadata:(DBMetadata *)metadata {
for (DBMetadata * child in metadata.contents) {
if (!child.isDirectory) {
NSString *fileName = [dbObject.path lastPathComponent];
if (![fileName isEqualToString:#"MyExample.txt"]) {
/* call dbupload if dbObject.lastModifiedDate > than your local file*/
}
}
}
}
I am developing an iPhone app with someone else. The app works fine for me, but he is running into a bug. We think this bug is related to the fact that he is getting multiple Application directories for this same app. In my ~/Library/Application Support/iPhone Simulator/User/Applications, I only have one folder at all times.
He says that he will get 3 or 4 directories when he is only working on this one app. We think this is our problem because our bug has to do with displaying images that are stored in the app's Documents folder. Does anyone know why he is ending up with multiple directories or how to stop it?
Edit:
Here is the code for writing the image to a file:
NSData *image = [NSData dataWithContentsOfURL:[NSURL URLWithString:[currentArticle articleImage]]];
NSArray *array = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *imagePath = [array objectAtIndex:0];
NSFileManager *NSFM = [NSFileManager defaultManager];
BOOL isDir = YES;
if(![NSFM fileExistsAtPath:imagePath isDirectory:&isDir])
if(![NSFM createDirectoryAtPath:imagePath attributes:nil])
NSLog(#"error");
imagePath = [imagePath stringByAppendingFormat:#"/images"];
if(![NSFM fileExistsAtPath:imagePath isDirectory:&isDir])
if(![NSFM createDirectoryAtPath:imagePath attributes:nil])
NSLog(#"error");
imagePath = [imagePath stringByAppendingFormat:#"/%#.jpg", [currentArticle uniqueID]];
[image writeToFile:imagePath atomically:NO];
And here is the code for getting the path when I need the image:
- (NSString *)imagePath
{
NSArray *array = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *imagePath = [array objectAtIndex:0];
return [imagePath stringByAppendingFormat:#"/images/%#.jpg", [self uniqueID]];
}
The app works great for me, but my partner says that the images don't show up intermittently, and he notices that he gets multiple directories in his Applications folder.
I had this problem (I was saving photos in the apps documents directory) and after every new build the directory get's renamed, so my paths were no longer valid. I cooked up these 2 functions (in my app delegate) that will give me a path for the file I want to save or load from the documents or temp directory. Even if the app directory changes, as long as you only store the file name and not the full path, and then use your helper functions to get the path when you need it later you will be ok. Here's my functions for this:
+ (NSString*)fullPathToFile:(NSString*)file {
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:file];
}
+ (NSString*)fullPathToTemporaryFile:(NSString*)file {
return [NSTemporaryDirectory() stringByAppendingPathComponent:file];
}
Works like a charm.
I am writing an iPhone app – a client for some social network. The app support multiple accounts. Info about accounts are stored in a keyed archive.
A method used for saving:
- (void) saveAccounts {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [paths objectAtIndex:0];
path = [path stringByAppendingPathComponent:#"accounts.bin"];
// NSMutableArray *accounts = ...;
[NSKeyedArchiver archiveRootObject:accounts toFile:path];
}
A method uses for reading:
- (NSMutableArray *) loadAccounts {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [paths objectAtIndex:0];
path = [path stringByAppendingPathComponent:#"accounts.bin"];
NSMutableArray *restoredAccounts = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
return [restoredAccounts retain];
}
The method saveAccounts is used only if some account is added/modified/deleted. The method loadAccounts is used every time the app starts. There isn't any other code that access this file.
I and one of my testers get an issue. At some moment the starts to act like accounts.bin is missing. If loadAccounts returns nil, the app offers to add an account. After I enter an account (the app must call saveAccounts), the app works normally, but when I launch it again, it asks me to add account again. The only solutions is too reinstall the app to iPhone, after reinstall it works for some time with any troubles.
We (I and my tester who get an issue) use iPhone 3G with 3.1.2.
An another tester who didn't experience this issue on his iPhone 3GS with 3.1.2.
Any ideas why this file disappears?
update
I found bug in my code. There was a code that deletes whole Document directory. Because this part of a code is a remote server related, it was hard to trace this code. Bug appeared under very rare conditions only.
Now the bug is found, and the code is corrected. wkw's answer didn't solved my problem, but it forced me to dig deeper. Thank you!
How about -- as a debugging device --verifying the contents of your Documents directory in loadAccounts or at least whenever the unarchiver returns nil. There's some code below to get the names of files in a directory. Set a breakpoint and just dump the NSArray to view the items.
If you can see that the file exists in the Docs dir, then your problem is elsewhere. Perhaps it did not successfully archive. check the return value on the call to archive the data:
if( ! [NSKeyedArchiver archiveRootObject:accounts toFile:path] ){
NSLog(#"Oooops! account data failed to write to disk");
}
Get names of files in directory:
- (NSArray*) directoryContentsNames:(NSString*) directoryPath {
NSArray* result;
{
result = [[NSFileManager defaultManager]
directoryContentsAtPath: directoryPath];
}
if( result && [result count] > 0 ){
NSMutableArray *items = [[[NSMutableArray alloc] init] autorelease];
for( NSString *name in result ){
if( ! [name isEqualToString:#".DS_Store"] )
[items addObject: name];
}
result = items;
}
return result;
}
Perhaps NSUserDefaults might be easier to use?
Also, is there a reason to use Keyed(Un)Archiver instead of NSArray's writeToFile?
if( ! [accounts writeToFile:path atomically:YES] )
; // do something since write failed
and to read the file in:
NSMutableArray *accounts = [[NSArray arrayWithContentsOfFile:path] mutableCopy];
I cache the images to the document directory of my app using the following code.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *saveDirectory = [paths objectAtIndex:0];
But even after I close the application, its still there. How can I clear it when I lose my app. I am doing all this in my simulator.
I would implement applicationWillTerminate: in my application delegate and remove the cache files there. Or better yet, as suggested by Vladimir, save them in a temporary directory and let the OS clean them up when needed.
- (void)applicationWillTerminate:(UIApplication *)app
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *cacheFiles = [fileManager contentsOfDirectoryAtPath:saveDirectory error:error];
for (NSString *file in cacheFiles) {
error = nil;
[fileManager removeItemAtPath:[saveDirectory stringByAppendingPathComponent:file] error:error];
/* handle error */
}
}
If you do not want your cached images to be preserved after application is closed better save them to temporary directory - they will be removed automatically.
If you want to manually remove the files you must store the paths for them and use the following NSFileManager function:
- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error
Edit: sorry, I appeared to be wrong here about automatic deleting. Here's a quote from Developing Guide:
Use this directory to write temporary files that you do not need to persist between launches of your application. Your application should remove files from this directory when it determines they are no longer needed. (The system may also purge lingering files from this directory when your application is not running.)
NSString *file;
NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:saveDirectory];
NSError* err;
while (file = [dirEnum nextObject]) {
err = nil;
[[NSFileManager defaultManager] removeItemAtPath:[saveDirectory stringByAppendingPathComponent:file] error:&err]];
if(err){
//print some errror message
}
}
Use the temporary directory path as specified in this question:
How can I get a writable path on the iPhone?