SkillAgentSearch skills...

UnzipKit

An Objective-C zlib wrapper for compressing and decompressing ZIP files

Install / Use

/learn @abbeycode/UnzipKit
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Build Status Documentation Coverage

About

UnzipKit is an Objective-C zlib wrapper for compressing and decompressing Zip files on OS X and iOS. It's based on the AgileBits fork of Objective-Zip, developed by Flying Dolphin Studio.

It provides the following over Objective-Zip:

  • A simpler API, with only a handful of methods, and no incantations to remember
  • The ability to delete files in an archive, including overwriting an existing file
  • Pervasive use of blocks, making iteration simple
  • Full documentation for all methods
  • Pervasive use of NSError, instead of throwing exceptions

Installation

UnzipKit supports both CocoaPods and Carthage. CocoaPods does not support dynamic framework targets (as of v0.39.0), so in that case, please use Carthage.

Cartfile:

github "abbeycode/UnzipKit"

Podfile:

pod "UnzipKit"

Deleting files

Using the method -deleteFile:error: currently creates a new copy of the archive in a temporary location, without the deleted file, then replaces the original archive. By default, all methods to write data perform a delete on the file name they write before archiving the new data. You can turn this off by calling the overload with an overwrite argument, setting it to NO. This will not remove the original copy of that file, though, causing the archive to grow with each write of the same file name.

If that's not a concern, such as when creating a new archive from scratch, it would improve performance, particularly for archives with a large number of files.

NSError *archiveError = nil;
UZKArchive *archive = [UZKArchive zipArchiveAtPath:@"An Archive.zip" error:&archiveError];
BOOL deleteSuccessful = [archive deleteFile:@"dir/anotherFilename.jpg"
                                      error:&error];

Detecting Zip files

You can quickly and efficiently check whether a file at a given path or URL is a Zip archive:

BOOL fileAtPathIsArchive = [UZKArchive pathIsAZip:@"some/file.zip"];

NSURL *url = [NSURL fileURLWithPath:@"some/file.zip"];
BOOL fileAtURLIsArchive = [UZKArchive urlIsAZip:url];

Reading Zip contents

NSError *archiveError = nil;
UZKArchive *archive = [UZKArchive zipArchiveAtPath:@"An Archive.zip" error:&archiveError];
NSError *error = nil;

You can use UnzipKit to perform these read-only operations:

  • List the contents of the archive

    NSArray<NSString*> *filesInArchive = [archive listFilenames:&error];
    
  • Extract all files to disk

    BOOL extractFilesSuccessful = [archive extractFilesTo:@"some/directory"
                                                overWrite:NO
                                                    error:&error];
    
  • Extract each archived file into memory

    NSData *extractedData = [archive extractDataFromFile:@"a file in the archive.jpg"
                                                   error:&error];
    

Modifying archives

NSError *archiveError = nil;
UZKArchive *archive = [UZKArchive zipArchiveAtPath:@"An Archive.zip" error:&archiveError];
NSError *error = nil;
NSData *someFile = // Some data to write

You can also modify Zip archives:

  • Write an in-memory NSData into the archive

    BOOL success = [archive writeData:someFile
                             filePath:@"dir/filename.jpg"
                                error:&error];
    
  • Write data as a stream to the archive (from disk or over the network), using a block:

    BOOL success = [archive writeIntoBuffer:@"dir/filename.png"
                                      error:&error
                                      block:
        ^BOOL(BOOL(^writeData)(const void *bytes, unsigned int length), NSError**(actionError)) {
            for (NSUInteger i = 0; i <= someFile.length; i += bufferSize) {
                const void *bytes = // some data
                unsigned int length = // length of data
    
                if (/* Some error occurred reading the data */) {
                    *actionError = // Any error that was produced, or make your own
                    return NO;
                }
    
                if (!writeData(&bytes, length)) {
                    return NO;
                }
            }
    
            return YES;
        }];
    
  • Delete files from the archive

    BOOL success = [archive deleteFile:@"No-good-file.txt" error:&error];
    

Progress Reporting

The following methods support NSProgress and NSProgressReporting:

  • extractFilesTo:overwrite:error:
  • extractData:error:
  • extractDataFromFile:error:
  • performOnFilesInArchive:error:
  • performOnDataInArchive:error:
  • extractBufferedDataFromFile:error:action:
  • writeData:filePath:error:*
  • writeData:filePath:fileDate:error:*
  • writeData:filePath:fileDate:compressionMethod:password:error:*
  • writeData:filePath:fileDate:compressionMethod:password:overwrite:error:*

_* the writeData... methods don't support cancellation like the read-only methods do

Using implicit NSProgress hierarchy

You can create your own instance of NSProgress and observe its fractionCompleted property with KVO to monitor progress like so:

    static void *ExtractDataContext = &ExtractDataContext;

    UZKArchive *archive = [[UZKArchive alloc] initWithURL:aFileURL error:nil];

    NSProgress *extractDataProgress = [NSProgress progressWithTotalUnitCount:1];
    [extractDataProgress becomeCurrentWithPendingUnitCount:1];
    
    NSString *observedSelector = NSStringFromSelector(@selector(fractionCompleted));
    
    [extractDataProgress addObserver:self
                          forKeyPath:observedSelector
                             options:NSKeyValueObservingOptionInitial
                             context:ExtractDataContext];
    
    NSError *extractError = nil;
    NSData *data = [archive extractDataFromFile:firstFile error:&extractError];

    [extractDataProgress resignCurrent];
    [extractDataProgress removeObserver:self forKeyPath:observedSelector];

Using your own explicit NSProgress instance

If you don't have a hierarchy of NSProgress instances, or if you want to observe more details during progress updates in extractFilesTo:overwrite:error:, you can create your own instance of NSProgress and set the UZKArchive instance's progress property, like so:

    static void *ExtractFilesContext = &ExtractFilesContext;

    UZKArchive *archive = [[UZKArchive alloc] initWithURL:aFileURL error:nil];
    
    NSProgress *extractFilesProgress = [NSProgress progressWithTotalUnitCount:1];
    archive.progress = extractFilesProgress;
    
    NSString *observedSelector = NSStringFromSelector(@selector(localizedDescription));
    
    [self.descriptionsReported removeAllObjects];
    [extractFilesProgress addObserver:self
                           forKeyPath:observedSelector
                              options:NSKeyValueObservingOptionInitial
                              context:ExtractFilesContext];
    
    NSError *extractError = nil;
    BOOL success = [archive extractFilesTo:extractURL.path
                                 overwrite:NO
                                     error:&extractError];
    
    [extractFilesProgress removeObserver:self forKeyPath:observedSelector];

Cancellation with NSProgress

Using either method above, you can call [progress cancel] to stop the operation in progress. It will cause the operation to fail, returning nil or NO (depending on the return type, and give an error with error code UZKErrorCodeUserCancelled.

Note: Cancellation is only supported on extraction methods, not write methods.

Documentation

Full documentation for the project is available on CocoaDocs.

Logging

For all OS versions from 2016 onward (macOS 10.12, iOS 10, tvOS 10, watchOS 3), UnzipKit uses the new Unified Logging framework for logging and Activity Tracing. You can view messages at the Info or Debug level to view more details of how UnzipKit is working, and use Activity Tracing to help pinpoint the code path that's causing a particular error.

As a fallback, regular NSLog is used on older OSes, with all messages logged at the same level.

When debugging your own code, if you'd like to decrease the verbosity of the UnzipKit framework, you can run the following command:

sudo log config --mode "level:default" --subsystem com.abbey-code.UnzipKit

The available levels, in order of increasing verbosity, are default, info, debug, with debug being the default.

Logging guidelines

These are the general rules governing the particulars of how activities and log messages are classified and written. They were written after the initial round of log messages were, so there may be some inconsistencies (such as an incorrect log level). If you think you spot one, open an issue or a pull request!

Logging

Log messages should follow these conventions.

  1. Log messages don't have final punctuation (like these list items)
  2. Messages that note a C function is about to be called, rather than a higher level UnzipKit or Cocoa method, end with "...", since it's not expected for them to log any details of their own

Default log level

There should be no messages at this level, so that it's possible for a consumer of the API to turn off all diagnostic logging from it, as de

View on GitHub
GitHub Stars85
CategoryDevelopment
Updated10mo ago
Forks21

Languages

Objective-C

Security Score

87/100

Audited on Jun 10, 2025

No findings