Unzipping Files In iOS Using ZipArchive

August 13th, 2012 Posted by: - posted under:Tutorials

In this tutorial, I am going to demonstrate how you can zip and unzip files from within your iOS applications. We will be using a third party library called ZipArchive to achieve this. While there are a couple solutions out there to zip and unzip files, I feel that the ZipArchive library was the fastest and easiest way to get up and running.

Why Would I want To Unzip Files?

That’s a great question. There are a number of reasons why you might want to support zipping and unzipping files inside of your applications. Here are just a few:

Apple’s 50 MB App Store Download Cap

Apple has imposed a 50MB download limit over 3G on applications downloaded to appease carriers and not hog all of their bandwidth. One way to circumvent this is to keep your binaries very small, and download the resources your application needs. The best way to package these resources is of course, a zip file. So the process (as you will see demoed below), is to open your app, check for resource updates, download the zip file, and unzip it. Now you can ship smaller applications, that fetch their resources dynamically.

Dynamic Updates To Content

I touched on this above. When your application needs updated assets, the common practice is to submit an update to the App Store. This could take up to a week for Apple to Review and publish. A faster approach is to zip your assets up and stick them on a server somewhere (even your Dropbox) and have your app download and unzip them. This way, whenever you want to make asset-based changes to your application, you won’t need to submit another copy of it to the store.

Downloading Zip Files From The Web

One huge downfall of Safari and the Mail app is, they are unable to open zipped files. It would be nice to offer some sort of support to view zipped archives on your device. Many of the “download” apps in the store support this, and you can too with some help from ZipArchive.

Setting Up Your Project

Head over to http://code.google.com/p/ziparchive/ and checkout a copy of ZipArchive. Or you can just type this into your terminal.

svn checkout http://ziparchive.googlecode.com/svn/trunk/ziparchive-read-only

Once you have cloned the repository, delete the MakeFile file from the minizip folder. We will let XCode build our files. Now, drag this minizip folder as well as the ZipArchive.h and ZipArchive.mm files into your project, making sure to check “Create groups for any added folders”. Also, be sure your target is checked.

Note: No ARC Support

If you are using an ARC enabled project, you will need to tell the compiler not to use ARC for ZipArchive. To do this, click on the project in the left hand column. Then click on your target in the middle column and select the “Build Phases” tab.

Expand the “Compile Sources” area, locate ZipArchive.mm and double click on it. In the box that pops up, type in -fno-objc-arc and click Done.

Linking libz

The last step is to link your project against libz.1.2.5.dylib. From the Build Phases screen you navigated to above, expand the Link Binary With Libraries section and click the “+” button to add a new library. Search the list for libz.1.2.5.dylib, select it and click Add.

Now, compile your project and it should succeed with no errors. One thing to note is ZipArchive might produce some warnings, they are not a big deal, but if you are a warning Nazi (you should be), dig into the code and see if you can solve them yourself.

Downloading And Unzipping Files

I will now show you how easy it is to download a zip file from the web, unzip its contents, and use them in your project. The method of which we will download the files is very basic and most likely wouldn’t be used in production without further error reporting and checking.

The sample project has a view that looks like this:

It’s basically a UIImageView and a UILabel. Inside of the view controller I have set up IBOutlets for these two items. Make sure and download the sample project to see the implementation details. We will be downloading a zip file from the web that contains an image and a text file which you can see displayed in the screenshot above. Once unzipped, the image will be set as the viewable image in our UIImageView and the textual contents of the .txt file will be displayed inside of the UILabel.

**1. Import the ZipArchive headers **

#import "ZipArchive.h"

2. Download the the zip file

    // 1 
    dispatch_queue_t queue = dispatch_get_global_queue(
                                                       DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        NSURL *url = [NSURL URLWithString:@"http://www.icodeblog.com/wp-content/uploads/2012/08/zipfile.zip"];
        NSError *error = nil;
        // 2
        NSData *data = [NSData dataWithContentsOfURL:url options:0 error:&error];
 
        if(!error)
        {        
            // 3
            NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
            NSString *path = [paths objectAtIndex:0];
            NSString *zipPath = [path stringByAppendingPathComponent:@"zipfile.zip"];
 
            [data writeToFile:zipPath options:0 error:&error];
 
            if(!error)
            {
                // TODO: Unzip
            }
            else
            {
                NSLog(@"Error saving file %@",error);
            }
        }
        else
        {
            NSLog(@"Error downloading zip file: %@", error);
        }
 
    });

This preliminary code downloads a zip file from iCodeBlog and saves it to the caches directory for the application.

  1. Creates a dispatch queue in which to run our code on with default priority.
  2. Quick and dirty way of fetching data from the web.
  3. Resolves the path to the caches directory and writes out the downloaded data to a local zip file

Now that you have it downloading the file to disk, it’s time to unzip that file and make use of it’s contents.

3. Unzipping the downloaded file

The last step here is to unzip the file you just downloaded. To be clear, it was save to the path /Library/Caches/zipfile.zip and once extracted, it’s contents will be inside of the Caches folder as well.

Replace the //TODO: Unzip in the code above, with the following code:

ZipArchive *za = [[ZipArchive alloc] init];
// 1
if ([za UnzipOpenFile: zipPath]) {      
    // 2      
    BOOL ret = [za UnzipFileTo: path overWrite: YES];
    if (NO == ret){} [za UnzipCloseFile];
 
    // 3
    NSString *imageFilePath = [path stringByAppendingPathComponent:@"photo.png"];
    NSString *textFilePath = [path stringByAppendingPathComponent:@"text.txt"];
    NSData *imageData = [NSData dataWithContentsOfFile:imageFilePath options:0 error:nil];
    UIImage *img = [UIImage imageWithData:imageData];
    NSString *textString = [NSString stringWithContentsOfFile:textFilePath 
        encoding:NSASCIIStringEncoding error:nil];
 
    // 4           
    dispatch_async(dispatch_get_main_queue(), ^{
        self.imageView.image = img;
        self.label.text = textString;
    });

Here is an explanation of what’s going on.

  1. Opens the file and unzips it in memory
  2. Writes the unzipped contents out to a given path (Caches folder)
  3. Makes use of the unzipped files
  4. Updates the UI (on the main thread of course) with the newly fetched data.

It really is that simple.

Zipping Files

Now you are going to see how to go the other way and zip up some files on disk. Again, this could be particularly handy when you want to allow your users to share groups of files via the web or email.

If you completed the steps above, your caches folder should have some files lying around that we can just zip up again and send off. We are going to zip up the two files you previously unzipped, stuff them into a new zip file, and write that out to the documents directory.

In my sample project, I have created a new button at the top that says “Zip Files” which links to an IBAction called zipFilesButtonPressed: when tapped. That is where I will be doing the zipping:

- (IBAction)zipFilesButtonPressed:(id)sender
{
    // 1
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *docspath = [paths objectAtIndex:0];
 
    // 2
    paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    NSString *cachePath = [paths objectAtIndex:0];
 
    // 3
    NSString *zipFile = [docspath stringByAppendingPathComponent:@"newzipfile.zip"];       
 
    // 4
    ZipArchive *za = [[ZipArchive alloc] init];
    [za CreateZipFile2:zipFile];
 
    // 5
    NSString *imagePath = [cachePath stringByAppendingPathComponent:@"photo.png"];
    NSString *textPath = [cachePath stringByAppendingPathComponent:@"text.txt"];
 
    // 6
    [za addFileToZip:imagePath newname:@"NewPhotoName.png"];
    [za addFileToZip:textPath newname:@"NewTextName.txt"];
 
    // 7
    BOOL success = [za CloseZipFile2];    
    NSLog(@"Zipped file with result %d",success);
 
}

Here is what’s going on:

  1. Resolve the path to the documents directory. We will need this when determining the path to write out our new zip file.
  2. Resolve the path to the caches directory. This is used to fetch the files that will be zipped up.
  3. Determines the path the zip file we will be writing out.
  4. Instantiates the ZipArchive object and tells it to create a new “in-memory” zip file. Note, the file won’t be written out until you call the corresponding CloseZipFile2 method.
  5. Resolve the path to the files that will be zipped up.
  6. Add the files to the zip archive. You can add as many files as you would like here. You can even add directories (ie you could have added the entire caches directory if you wanted to).
  7. Write out the zip file and close it. Just to test, we log the result to ensure that the zipping was successful.

After running through the application, tapping on the “Zip Files” button, look inside of the Application’s Documents folder (located ~/Library/Application Support/iPhone Simulator/[iOS Version]/Applications/[Unique ID]/Documents]. It should contain a single zip file called newzipfile.zip. If you unzip it, you should see the two files that you stuffed in there extracted.

Conclusion

You have now seen how to zip and unzip files on iOS devices using the ZipArchive library. You can download the sample project at the bottom of this post. As always, if you have any questions or comments, feel free to write them in the comments of this post or write them to me on Twitter @brandontreb.

Happy iCoding!

Download The Sample Project