Swiftpack.co - Package - weichsel/ZIPFoundation

Swift Package Manager compatible Carthage compatible CocoaPods Compatible Platform Twitter

ZIP Foundation is a library to create, read and modify ZIP archive files.
It is written in Swift and based on Apple's libcompression for high performance and energy efficiency.
To learn more about the performance characteristics of the framework, you can read this blog post.


  • ☑ Modern Swift API
  • ☑ High Performance Compression and Decompression
  • ☑ Deterministic Memory Consumption
  • ☑ Linux compatibility
  • ☑ No 3rd party dependencies (on Apple platforms, zlib on Linux)
  • ☑ Comprehensive Unit and Performance Test Coverage
  • ☑ Complete Documentation


  • iOS 9.0+ / macOS 10.11+ / tvOS 9.0+ / watchOS 2.0+
  • Or Linux with zlib development package
  • Xcode 10.0
  • Swift 4.0


Swift Package Manager

The Swift Package Manager is a dependency manager integrated with the Swift build system. To learn how to use the Swift Package Manager for your project, please read the official documentation.
To add ZIP Foundation as a dependency, you have to add it to the dependencies of your Package.swift file and refer to that dependency in your target.

// swift-tools-version:5.0
import PackageDescription
let package = Package(
    name: "<Your Product Name>",
    dependencies: [
		.package(url: "https://github.com/weichsel/ZIPFoundation.git", .upToNextMajor(from: "0.9.0"))
    targets: [
		name: "<Your Target Name>",
		dependencies: ["ZIPFoundation"]),

After adding the dependency, you can fetch the library with:

$ swift package resolve


Carthage is a decentralized dependency manager.
Installation instructions can be found in the project's README file.

To integrate ZIPFoundation into your Xcode project using Carthage, you have to add it to your Cartfile:

github "weichsel/ZIPFoundation" ~> 0.9

After adding ZIPFoundation to the Cartfile, you have to fetch the sources by running:

carthage update --no-build

The fetched project has to be integrated into your workspace by dragging ZIPFoundation.xcodeproj to Xcode's Project Navigator. (See official Carhage docs.)


CocoaPods is a dependency manager for Objective-C and Swift.
To learn more about setting up your project for CocoaPods, please refer to the official documentation.
To integrate ZIP Foundation into your Xcode project using CocoaPods, you have to add it to your project's Podfile:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '10.0'
target '<Your Target Name>' do
    pod 'ZIPFoundation', '~> 0.9'

Afterwards, run the following command:

$ pod install


ZIP Foundation provides two high level methods to zip and unzip items. Both are implemented as extension of FileManager.
The functionality of those methods is modeled after the behavior of the Archive Utility in macOS.

Zipping Files and Directories

To zip a single file you simply pass a file URL representing the item you want to zip and a destination URL to FileManager.zipItem(at sourceURL: URL, to destinationURL: URL):

let fileManager = FileManager()
let currentWorkingPath = fileManager.currentDirectoryPath
var sourceURL = URL(fileURLWithPath: currentWorkingPath)
var destinationURL = URL(fileURLWithPath: currentWorkingPath)
do {
    try fileManager.zipItem(at: sourceURL, to: destinationURL)
} catch {
    print("Creation of ZIP archive failed with error:\(error)")

By default, archives are created without any compression. To create compressed ZIP archives, the optional compressionMethod parameter has to be set to .deflate.
The same method also accepts URLs that represent directory items. In that case, zipItem adds the directory content of sourceURL to the archive.
By default, a root directory entry named after the lastPathComponent of the sourceURL is added to the destination archive. If you don't want to preserve the parent directory of the source in your archive, you can pass shouldKeepParent: false.

Unzipping Archives

To unzip existing archives, you can use FileManager.unzipItem(at sourceURL: URL, to destinationURL: URL).
This recursively extracts all entries within the archive to the destination URL:

let fileManager = FileManager()
let currentWorkingPath = fileManager.currentDirectoryPath
var sourceURL = URL(fileURLWithPath: currentWorkingPath)
var destinationURL = URL(fileURLWithPath: currentWorkingPath)
do {
    try fileManager.createDirectory(at: destinationURL, withIntermediateDirectories: true, attributes: nil)
    try fileManager.unzipItem(at: sourceURL, to: destinationURL)
} catch {
    print("Extraction of ZIP archive failed with error:\(error)")

Advanced Usage

ZIP Foundation also allows you to individually access specific entries without the need to extract the whole archive. Additionally it comes with the ability to incrementally update archive contents.

Accessing individual Entries

To gain access to specific ZIP entries, you have to initialize an Archive object with a file URL that represents an existing archive. After doing that, entries can be retrieved via their relative path. Archive conforms to Sequence and therefore supports subscripting:

let fileManager = FileManager()
let currentWorkingPath = fileManager.currentDirectoryPath
var archiveURL = URL(fileURLWithPath: currentWorkingPath)
guard let archive = Archive(url: archiveURL, accessMode: .read) else  {
guard let entry = archive["file.txt"] else {
var destinationURL = URL(fileURLWithPath: currentWorkingPath)
do {
    try archive.extract(entry, to: destinationURL)
} catch {
    print("Extracting entry from archive failed with error:\(error)")

The extract method accepts optional parameters that allow you to control compression and memory consumption.
You can find detailed information about that parameters in the method's documentation.

Creating Archives

To create a new Archive, pass in a non-existing file URL and AccessMode.create.

let currentWorkingPath = fileManager.currentDirectoryPath
var archiveURL = URL(fileURLWithPath: currentWorkingPath)
guard let archive = Archive(url: archiveURL, accessMode: .create) else  {

Adding and Removing Entries

You can add or remove entries to/from archives that have been opened with .create or .update AccessMode. To add an entry from an existing file, you can pass a relative path and a base URL to addEntry. The relative path identifies the entry within the ZIP archive. The relative path and the base URL must form an absolute file URL that points to the file you want to add to the archive:

let fileManager = FileManager()
let currentWorkingPath = fileManager.currentDirectoryPath
var archiveURL = URL(fileURLWithPath: currentWorkingPath)
guard let archive = Archive(url: archiveURL, accessMode: .update) else  {
var fileURL = URL(fileURLWithPath: currentWorkingPath)
do {
    try archive.addEntry(with: fileURL.lastPathComponent, relativeTo: fileURL.deletingLastPathComponent())
} catch {
    print("Adding entry to ZIP archive failed with error:\(error)")

The addEntry method accepts several optional parameters that allow you to control compression, memory consumption and file attributes.
You can find detailed information about that parameters in the method's documentation.

To remove an entry, you need a reference to an entry within an archive that you can pass to removeEntry:

guard let entry = archive["file.txt"] else {
do {
    try archive.remove(entry)
} catch {
    print("Removing entry from ZIP archive failed with error:\(error)")

Closure based Reading and Writing

ZIP Foundation also allows you to consume ZIP entry contents without writing them to the file system. The extract method accepts a closure of type Consumer. This closure is called during extraction until the contents of an entry are exhausted:

try archive.extract(entry, consumer: { (data) in

The data passed into the closure contains chunks of the current entry. You can control the chunk size of the entry by providing the optional bufferSize parameter.

You can also add entries from an in-memory data source. To do this you have to provide a closure of type Provider to the addEntry method:

guard let data = "abcdefghijkl".data(using: .utf8) else { return }
try? archive.addEntry(with: "fromMemory.txt", type: .file, uncompressedSize: 12, bufferSize: 4, provider: { (position, size) -> Data in
    // This will be called until `data` is exhausted (3x in this case).
    return data.subdata(in: position..<position+size)

The closure is called until enough data has been provided to create an entry of uncompressedSize. The closure receives position and size arguments so that you can manage the state of your data source.

In-Memory Archives

Besides closure based reading and writing of file based archives, ZIP Foundation also provides capabilities to process in-memory archives. This allows creation or extraction of archives that only reside in RAM. One use case for this functionality is dynamic creation of ZIP archives that are later sent to a client - without performing any disk IO.

To work with in-memory archives the init(data: Data, accessMode: AccessMode) initializer must be used.
To read or update an in-memory archive, the passed-in data must contain a representation of a valid ZIP archive.
To create an in-memory archive, the data parameter can be omitted:

guard let archive = Archive(accessMode: .create),
        let data = "Some string!".data(using: .utf8) else { return }
    try? archive.addEntry(with: "inMemory.txt", type: .file, uncompressedSize: 12, bufferSize: 4, provider: { (position, size) -> Data in
        return data.subdata(in: position..<position+size)
let archiveData = archive.data

Progress Tracking and Cancellation

All Archive operations take an optional progress parameter. By passing in an instance of Progress, you indicate that you want to track the progress of the current ZIP operation. ZIP Foundation automatically configures the totalUnitCount of the progress object and continuously updates its completedUnitCount.
To get notifications about the completed work of the current operation, you can attach a Key-Value Observer to the fractionCompleted property of your progress object.
The ZIP Foundation FileManager extension methods also accept optional progress parameters. zipItem and unzipItem both automatically create a hierarchy of progress objects that reflect the progress of all items contained in a directory or an archive that contains multiple items.

The cancel() method of Progress can be used to terminate an unfinished ZIP operation. In case of cancelation, the current operation throws an ArchiveError.cancelledOperation exception.


ZIP Foundation is written and maintained by Thomas Zoechling.
Twitter: @weichsel.


ZIP Foundation is released under the MIT License.
See LICENSE for details.


Stars: 1355



0.9.11 - 2020-03-26 22:24:47


  • Read/Write support for in-memory archives


  • Fixed a memory safety issue during (de)compression
  • Fixed dangling pointer warnings when serializing ZIP internal structs to Data
  • Fixed missing Swift 5 language version when integrating via CocoaPods
  • Fixed inconsistent usage of the optional preferredEncoding parameter during entry addition
  • Improved documentation for compression settings

0.9.10 - 2019-12-02 18:57:31


  • Optional skipCRC32 parameter to speed up entry reading


  • Fixed a race condition during archive creation or extraction
  • Fixed an error when trying to add broken symlinks to an archive
  • Fixed an App Store submission issue by updating the product identifier to use reverse DNS notation
  • Improved CRC32 calculation performance
  • Improved entry replacement performance on separate volumes
  • Improved documentation for closure-based writing

0.9.9 - 2019-04-10 10:37:00


  • Swift 5.0 support
  • Optional preferredEncoding parameter to explicitly configure an encoding for filepaths


  • Fixed a library load error related to dylib versioning
  • Fixed a hang during read when decoding small, .deflate compressed entries
  • Improved Linux support
  • Improved test suite on non-Darwin platforms

0.9.8 - 2019-01-21 15:12:27


  • Disabled symlink resolution during path traversal checking

0.9.7 - 2019-01-21 15:11:57


  • App extension support
  • Optional compressionMethod paramter for zipItem:


  • Fixed a path traversal attack vulnerability
  • Fixed a crash due to wrong error handling after failed fopen calls


  • Temporarily removed the currently unsupported .modificationDate attribute on non-Darwin platforms

0.9.6 - 2018-04-24 19:33:27


  • Swift 4.1 support


  • Fixed default directory permissions
  • Fixed a compile issue when building on Linux

0.9.5 - 2018-02-09 09:26:36


  • Progress tracking support
  • Operation cancellation support


  • Improved performance of CRC32 calculations
  • Improved Linux support
  • Fixed wrong behaviour when using the shouldKeepParent flag
  • Fixed a linker error during archive builds when integrating via Carthage

0.9.4 - 2017-12-08 08:25:53


  • Fixed a wrong setting for FRAMEWORK_SEARCH_PATHS that interfered with code signing
  • Added a proper value for CURRENT_PROJECT_VERSION to make the framework App Store compliant when using Carthage

0.9.3 - 2017-11-07 23:00:54


  • Carthage support


  • Improved error handling
  • Made consistent use of Swift's CocoaError instead of NSError

0.9.2 - 2017-08-05 09:58:36


  • Changed default POSIX permissions when file attributes are missing
  • Improved docs
  • Fixed a compiler warning when compiling with the latest Xcode 9 beta

0.9.1 - 2017-07-10 16:20:32


  • Optional parameter to skip CRC32 checksum calculation


  • Tweaked POSIX buffer sizes to improve IO and compression performance
  • Improved source readability
  • Refined documentation


  • Optional parameter to skip decompression during entry retrieval

0.9.0 - 2017-06-27 00:30:55

  • Initial release of ZIP Foundation.