Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

larswik

macrumors 68000
Original poster
Sep 8, 2006
1,552
11
I read the doc's tonight on NSDocumentDirectory and they had this example bellow. I am confused as to why it is creating an NSArray? the instance variable is 'paths' and looking at other material when you define paths you use NSStrings.

The next line does create an NSString pointer which points to the first index of the paths array, why an array? Is it not a normal file directory? Must everything be stored in an array before it can be stored in the NSDocumentDirectory?

Code:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

NSString *documentsDirectory = [paths objectAtIndex:0];
 
NSSearchPathForDirectoriesInDomains can return multiple paths. In this code, you're just arbitrarily taking just the first path returned.
 
You have to consider how you are obtaining NSDocumentsDirectory. Specifically, you are using the utility function NSSearchPathForDirectoriesInDomains, which under some circumstances (i.e. when you ask it for a particular type of directory and/or domain) can effectively return more than one thing. Obviously, returning more than one thing requires it to have a return type of NSArray (or some kind of collection), so whether you request something that should return one thing or 10 things, it has to return an NSArray.
 
On iOS it will only ever return a single value in the array (unless something changes). On MacOS X it can return multiple values.

You can easily write your own wrapper function that simply returns the single path as a string. This will make your code a little neater.
 
I'm getting it. I thought by reading the doc's that I would be writing NSArrays to the directory. But it sounds like if I want to store 10 jpg images in a folder on the iPhone NSDocumentDirectory downloaded from the web, I can do this. But when I want to retrieve the images I need to load them in to an NSArray. But I am assuing you can store NSDictionayrs or NSArrays in the NSDocumentsDirectory too.

Thanks!
 
I'm getting it. I thought by reading the doc's that I would be writing NSArrays to the directory. But it sounds like if I want to store 10 jpg images in a folder on the iPhone NSDocumentDirectory downloaded from the web, I can do this. But when I want to retrieve the images I need to load them in to an NSArray. But I am assuing you can store NSDictionayrs or NSArrays in the NSDocumentsDirectory too.

Thanks!

Either you're grossly confused, or this post is grossly confusing.

You cannot store NSArray, NSDictionary, or any kind of objects in NSDocumentsDirectory. NSDocumentDirectory is just a constant defined in NSPathUtilities.h.

If you mean that actual documents directory in the iOS file-system, this is just a container for files and other (sub-)directories. You cannot store objects in a file-system directory. You can archive objects into a file in a directory. You can enumerate over the files and subdirectories of a directory using the likes of NSFileManager.

How you load 10 jpegs in the document directory depends on what technique you're using to load them. This has nothing to do with the fact the NSSearchPathForDirectoriesInDomains returns an NSArray.
 
Jim, Sorry that was a dumb question. I understand that I can not store NSArrays and so on in a directory. Just like on my Mac I can not store those items. But I can store them in a plist file which I can store on the drive.

But if I do understand it right, also from what you said, I can store sound files, images, textfiles in the directory for my app on an iPhone because it is a directory kind of like a Mac OSX directory?

Thanks again Jim
 
But if I do understand it right, also from what you said, I can store sound files, images, textfiles in the directory for my app on an iPhone because it is a directory kind of like a Mac OSX directory?

Yes. The documents directory in iOS is free for you to use as you will. It's also inside your app's sandbox, so you there won't be anything in it except that your app put in there. For example, other apps have other documents directories in their own sandboxes.
 
Thanks Jim, I was playing with it tonight and I got it to write to the directory just fine. I was able to read the file back in to test, but instead of the label text field displaying the contents of the text file, it is displaying the path to the file. NSString * theNames I thought would contain the contents of the file?
2011-09-12 00:20:58.201 iphoneNSDirect[734:707] Documents directory: (
"nameTests.txt"
)
2011-09-12 00:20:58.206 iphoneNSDirect[734:707] TheNames: /var/mobile/Applications/B33546F8-B373-4ADF-B416-7E528A91FB0A/Documents/programming/iphoneNSDirect/iphoneNSDirect/nameTests.txt

The code
Code:
- (IBAction)pushButton:(id)sender {
    NSFileManager *filemgr = [NSFileManager defaultManager];
    
    NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES); // create path to theDirectory
    NSString *documentsDirectory = [paths objectAtIndex:0];
    
    NSLog(@"Documents directory: %@",[filemgr contentsOfDirectoryAtPath:documentsDirectory error:nil]); //writes out what is in directory
    
    NSString *pathToNames = [documentsDirectory stringByAppendingPathComponent:@"nameTests.txt"]; //looks for this file in the Directory
    NSString *theNames = [[NSString alloc] initWithContentsOfFile: pathToNames encoding: NSUTF8StringEncoding error: nil];
    
    NSLog(@"TheNames: %@", theNames); // writes out the string

    lableTextField.text = theNames; //the text lable displays the result
    [theNames release];
}
 
In Xcode I went to File/New and created an Empty file and saved it as nameTests.txt. This file was then saved in my project folder. I also included it for you to see. But what is in the file is just 3 lines of text.

Casa Blanca
Fishouse
Boathouse
 

Attachments

  • nameTests.txt
    31 bytes · Views: 89
Code:
- (IBAction)pushButton:(id)sender {
    NSFileManager *filemgr = [NSFileManager defaultManager];
    
    NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES); // create path to theDirectory
    NSString *documentsDirectory = [paths objectAtIndex:0];
    
    NSLog(@"Documents directory: %@",[filemgr contentsOfDirectoryAtPath:documentsDirectory error:nil]); //writes out what is in directory
    
    NSString *pathToNames = [documentsDirectory stringByAppendingPathComponent:@"nameTests.txt"]; //looks for this file in the Directory
    NSString *theNames = [[NSString alloc] initWithContentsOfFile: pathToNames encoding: NSUTF8StringEncoding error: nil];
    
    NSLog(@"TheNames: %@", theNames); // writes out the string

    lableTextField.text = theNames; //the text lable displays the result
    [theNames release];
}

This code works for me when connected to a "Read File" button. Although I created the file on the device (via a Create File button). But even if the file doesn't exist, I don't get the file name, I just get theName being nil.
 
That is weird. I must be doing something else wrong if it is working for you. Here is the project I compressed down. It must be something simple since it is displaying the path to the file and not the contents of the file.

Thanks
 

Attachments

  • iphoneNSDirect.zip
    46.5 KB · Views: 103
The problem is exactly what I figured it would be. Your writeToFile method, which I assume was meant to copy the nameTests.txt file from the app bundle to the documents directory, does not do that. Instead, it creates a new file called nameTests.txt whose contents is its own path (i.e. exactly what you see being printed out to the console).
 
Ahhh. So the problem is not reading the file from the directory on the iphone but the way that it is written to the directory. In this case I never wrote the text file correctly.

Let me break my method writeToFile down so I can learn what I am doing wrong. I deleted some of the code from the the method like NSLogs so I can just show the core components.
Code:
-(void)writeToFile{
    NSString *documentsDirectory = [NSHomeDirectory()  stringByAppendingPathComponent:@"Documents"]; // Point to Document directory
The code above creates an NSString pointer that stores the path to Documents directory on my iPhone.
Code:
    NSString *fileSavePath = [@"~/Documents/programming/iphoneNSDirect/iphoneNSDirect/nameTests.txt" stringByExpandingTildeInPath];
The code above I create a NSString pointer called fileSavePath which points to the file in my computer.
Code:
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:@"nameTests.txt"];
The code above is what I suspect is my problem area. I thought it was creating a pointer called filePath which assigns the documentDirectory as the path and saves the file named nameTests.txt to that location.
Code:
[fileSavePath writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
}
The code above handles the actual writing of the file to the directory on the iPhone.
 
Let's slow down a second here, because it seems you are missing a key step in this whole process.

Am I correct in thinking that you are trying to take the nameTests.txt file that you included in your project (the same one you attached in this thread earlier), and copy it to an identical file in your application's Documents directory?
 
OK, so the first thing you should notice is that at no point do you reference the existing file that you want to copy. That file is located in the application bundle, whose path you can get by doing this:

Code:
NSString *bundleTextPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"nameLists.txt"];

It looks like you might have mistakenly thought that the path of this file would be the same as the path of the file on your desktop file system (i.e. fileSavePath). In any case, your filePath NSString does appear to contain the correct path in the app's Documents directory where I assume you want the file to end up. So now the question is how do we get it there. You can either use the copyItemAtPath method of NSFileManager, or to follow a bit closer to your original code, read the file at bundleTextPath into an NSString, and then write it back out to filePath.

I will tell you that this:
Code:
[bundleTextPath writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
will not work, because you would be writing a file's path to another file, and not a file to another file. That is, NSString has no idea that bundleTextPath is actually a path to a text file, so it's not going to open that file and then write it out to filePath. You have to use some other NSString method to get the contents of bundleTextPath into an NSString first, then use writeToFile on that string.
 
Huh, when you write that I am baffled that I missed the whole linking to my file in my computer in the Bundle.

You have given me lots of food for thought and thank you for giving me a push and not giving me the answer to this directly. My goal tonight is to try to fill in the blanks to make this work. That way I learn it!

The over all goal is to have a file on my server to be downloaded 1 time a day. Then from the list of clients in the txt document I would create a UITable. This way I can add and remove clients by just changing the list.

Thank you again!
 
I went back to the beginning and was trying again tonight to get this. I used NSLog to display the paths and wanted to check to see if the NSBundle MainBundle is right? But the NSLog result seems to be on the iPhone and not the project folder on my Mac if I am reading it right?

2011-09-12 22:49:32.346 iphoneNSDirect[1504:707] paths: (
"/var/mobile/Applications/B33546F8-B373-4ADF-B416-7E528A91FB0A/Documents"
)
2011-09-12 22:49:32.356 iphoneNSDirect[1504:707] main Bundle: /var/mobile/Applications/B33546F8-B373-4ADF-B416-7E528A91FB0A/iphoneNSDirect.app/nameTests.txt

Is that correct? or should the NSBundle MainBundle be something like /Users/larspro/Documents/programming/iphoneNSDirect/iphoneNSDirect/nameTests.txt or should it start with var/mobile/Applications/.......

Here is the code so far
Code:
-(void)writeToFile{
    NSFileManager *fileManager = [NSFileManager defaultManager];
    
    NSArray *paths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSLog(@"paths: %@", paths); //path to Documents in iphone
    
    NSString *resourceForFile = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"nameTests.txt"];
    NSLog(@"main Bundle: %@", resourceForFile); //path to Documents in iphone

}
 
The path that you are seeing is correct. While nameTests.txt might exist in /Users/larspro/Documents/programming/iphoneNSDirect/iphoneNSDirect/, when you actually build the app it is copied inside of the app bundle, and that is where you should be accessing it from (and of course on a device you would have no choice) in order to copy it into the Documents directory.
 
I don't know why this is giving me so much grief trying to understand this read write concept to the ios.

The next line of code I added was to just copy over the file with an NSFileManager that you made mention to admanimal. I then found online an Enumerator to list what is in the directory. I created a new txt file and tried to copy that in so I should have 2 files in there. but when the enumerator runs it only sees the 1 file still. So it is still not correct.

Code:
-(void)writeToFile{
    NSFileManager *fileManager = [NSFileManager defaultManager];
    
    NSArray *paths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSLog(@"paths: %@", paths); //path to Documents in iphone
    
    NSString *resourceForFile = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"nameTestsTwo.txt"];
    NSLog(@"main Bundle: %@", resourceForFile); //path to Documents in iphoneerror:nil];
    
    [fileManager copyItemAtPath:resourceForFile toPath:documentsDirectory error:nil];
    
    NSFileManager *manager = [NSFileManager defaultManager];
    NSDirectoryEnumerator *direnum = [manager enumeratorAtPath:documentsDirectory];
    
    NSString *filename;
    
    while ((filename = [direnum nextObject] )) {
        NSLog(@"Item: %@",filename);
            
    }
}
 
When you use copyItemAtPath:toPath:error: the path parameters must include the file name. You can't just specify a directory where you want the toPath to go. Also, it's a good idea to check the value that's returned and the error parameter for help to figure out if/why it's not working.
 
I will try that. I went through and read the doc's again after reading your post for that method. The description does mention that I need the title as well. So when you write a file you need to tell it what it is and when you copy a file you need to do the same thing. The doc's also said to make sure nothing exists at the destination path that already has that name. The must also be an over write method as well.

I don't know why I am struggling with this so much with this concept.

Thanks
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.