iOS - Objective-C - Working with Files

Stephen Zaharuk / Monday, October 14, 2013

An app in iOS has it's own sandbox that it's allowed to work within. In that sandbox though, you're allowed to save and write files at your leisure. In this post I'll discuss how to do basic operations for saving a file, building a file path, and providing end user access to it. 

Directories

Lets start off by finding walking through the different directories we have access to, and what each directory allows us to do. 

The important method here is: NSSearchPathForDirectoriesInDomains() It takes 3 parameters. 

1. For this discussion, we're going to pass in one of two values:

    a. NSLibraryDirectory - Use this for saving app files that you do not want to expose to your 

                                       end users. 

    b. NSDocumentDirectory -  Use this if you want to allow your end users to be able to access        

                                      files and provide files via iTunes.

2. The next parameter, is the domain in which to search. Again, for the purpose of this post, we're going to use just one value: NSUserDomainMask

3. Finally, whether or not it should resolve the ~. In this case we're going to use YES. 

This method will return an Array of paths: 

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);

From this array, we're going to grab the first value. As thats the root path to the directory we're looking for.

NSString* rootPath = paths[0];

Now, that we have our starting point, we can build out the path to access/save the files we want to use. 

Working with strings to build paths 

The NSString class in objective-c has quite a few helper methods that make path building very easy. 

I'll walk you through a few of the most useful ones. 

Since we're building a path, you're most likely going to want to add a new path part. For that use: 

NSString* path = [rootPath stringByAppendingPathComponent:@"MyDocuments"];

This made our path go from: 

/var/mobile/Applications/F29D33C5-D78A-4D72-8A2B-773A6577FE1D/Library/

to 

/var/mobile/Applications/F29D33C5-D78A-4D72-8A2B-773A6577FE1D/Library/MyDocuments

Now, say we wanted to add a file name to this:

path = [path stringByAppendingPathComponent:@"MyFile"];
path = [path stringByAppendingPathExtension:@"data"];

The new path now looks like:

/var/mobile/Applications/F29D33C5-D78A-4D72-8A2B-773A6577FE1D/Library/MyDocuments/MyFile.data

Working with strings to read paths 

As you can see building a path is pretty simple. Lets take a look at deconstructing a path now:

First, lets look up the extension of a given path:

NSString* ext = [path pathExtension];

If we used the path we created above, this code will return "data". 

If you wanted, you could also break down a path into all of it's components:

NSArray* comps = [path pathComponents];

Using the following path, we would get an array with 8 values:

/var/mobile/Applications/F29D33C5-D78A-4D72-8A2B-773A6577FE1D/Library/MyDocuments/MyFile.data

"/" , "var", "mobile", "Applications", "F29D33C5-D78A-4D72-8A2B-773A6577FE1D", "Library", "MyDocuments", and "MyFile.data"

So, if you wanted to get the full file name of a file, it would look something like this:

NSString* fileName = comps[comps.count -1];

Now, say you wanted just the name of the file, without the extension. To do that, you would use the following method on NSString: stringByDeletingPathExension

NSString* nameWithoutExtension = [fileName stringByDeletingPathExtension];

And this of course returns "MyFile"

Working with the FileManager

Now that we know how to work with paths, lets put them to use. To do this, we're going to use a class called NSFileManager. This class has a static method, which returns an instance of itself, that we're going to use: 

NSFileManager* manager = [NSFileManager defaultManager];

This manager is what we will user for all of the following operations:

Check to see if a File/Directory exists:

if ([manager fileExistsAtPath:filePath])
{
   // Do Something
}

Create a Directory

NSError *error;
if ([manager createDirectoryAtPath:dir withIntermediateDirectories:YES attributes:nil error:&error])
{
// Do something
}
else
{
NSLog(@"Create directory error: %@", error);
}

Delete a File/Directory

if ([manager fileExistsAtPath:filePath]) // Make sure it exists
{
   [manager removeItemAtPath:filePath error:nil]; // Delete it
}

Copy File

NSError* error;
if([manager copyItemAtPath:path toPath:newPath error:&error])
{
   // Success!
}
else
{
   NSLog(@"%@", error);
}

Move File

NSError* error;
if([manager moveItemAtPath:toPath:error toPath:newPath error:&error])
{
   // Success!
}
else
{
   NSLog(@"%@", error);
}

One last thing I should mention regarding file paths, is that you may notice that some methods take NSURL objects instead of NSString objects. Luckily, there is an easy conversion from NSString to NSURL:

NURL* urlPath = [NSURL fileURLWithPath:stringPath];

iTunes Access to your Application's Library Directory

As i mentioned earlier, you could use NSDocumentsDirectory or NSLibraryDirectory to save your files to. When you use NSDocumentDirectory, you can allow end users to access any files you save, and also allow them to provide files for you to access. 

To allow this, there is a setting i the plist file of your application that you need to turn on:

"Application supports iTunes file sharing" And you need to set the value to YES. 

Once you do that, you can build your application and open up iTunes. Next, select your device, and navigate to the "Apps" tab. Now scroll down to the section called "File Sharing". Your app will now appear  in the list. Select it, and you should see all files that exist in the Library folder. The user can copy files out, and also drag files in. 

That concludes this tutorial on the File System. I hope it was useful!

By Stephen Zaharuk (SteveZ)