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:
NSError
, instead of throwing exceptionsUnzipKit 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"
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];
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];
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];
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];
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
NSProgress
hierarchyYou 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];
NSProgress
instanceIf 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];
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.
Full documentation for the project is available on CocoaDocs.
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.
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!
Log messages should follow these conventions.
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 detailed above. It's only possible to log config --mode "level:off"
for a process, not a subsystem.
Info level log statements serve the following specific purposes.
Most messages fall into this category, making it extremely verbose. All non-error messages that don't fall into either of the other two categories should be debug-level, with some examples of specific cases below.
NSError
generated should get logged with the same detail message as the NSError
object itselfNSError
log messages should contain the string of the error code's enumeration value (e.g. "UZKErrorCodeArchiveNotFound"
) when it is known at design timeSo far, there is only one case that gets logged at Fault-level: when a Cocoa framework methods that come back with an error
activity
, with more specific labels given to sub-activitiesNew tagged builds (in any branch) get pushed to CocoaPods automatically, provided they meet the following criteria:
#.#.#(-beta#)
, e.g. 2.9 or 2.9-beta5)pod spec lint
passes, making sure the CocoaPod is 100% validBefore pushing a build, you must:
Run set-version, like so:
./Scripts/set-version.sh <version number>
This does the following:
Once that's done, you can call git push --follow-tags
1, and let Travis CI take care of the rest.
1: Or set followTags = true
in your git config to always get this behavior:
git config --global push.followTags true