ZIPFoundation

repository·development·Indexed 25 days ago

https://github.com/weichsel/zipfoundation

A high-performance Swift library for creating, reading, and modifying ZIP archive files. It leverages Apple's libcompression and supports macOS, iOS, tvOS, watchOS, visionOS, and Linux. The library provides functionality for zipping and unzipping items via FileManager, managing in-memory archives, and manipulating individual archive entries using the Archive and Entry types.

Tokens
3.5K
Snippets
13
Records
19
Agent score
33%

What's inside ZIPFoundation

  1. Install ZIPFoundation via Swift Package Manager

    development

    To add ZIPFoundation as a dependency in your Swift project, add it to the dependencies array of your Package.swift file and include it in your target's dependencies. After updating the file, run swift package resolve to fetch the library.

    // 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: [
            .target(
    		name: "<Your Target Name>",
    		dependencies: ["ZIPFoundation"]),
        ]
    )
    $ swift package resolve
  2. Track and cancel ZIP operations with Progress

    development

    All Archive operations accept an optional progress parameter of type Progress.

    • Tracking: ZIP Foundation automatically configures totalUnitCount and updates completedUnitCount. You can observe fractionCompleted using Key-Value Observation (KVO).
    • Cancellation: Call cancel() on the Progress instance to terminate an operation. This will cause the operation to throw an ArchiveError.cancelledOperation error.
  3. Install ZIPFoundation via CocoaPods

    development

    Add ZIPFoundation to your project's Podfile and then run pod install. Ensure you are using use_frameworks! in your Podfile.

    source 'https://github.com/CocoaPods/Specs.git'
    platform :ios, '10.0'
    use_frameworks!
    target '<Your Target Name>' do
        pod 'ZIPFoundation', '~> 0.9'
    end
    $ pod install
  4. Install ZIPFoundation via Carthage

    development
    Add ZIPFoundation to your Cartfile using the following syntax: github "weichsel/ZIPFoundation" ~> 0.9. Then, run carthage update --no-build to fetch the sources. Finally, integrate the framework into your Xcode workspace by dragging ZIPFoundation.xcodeproj into the Project Navigator.
    github "weichsel/ZIPFoundation" ~> 0.9
    carthage update --no-build
  5. Zip files and directories using FileManager.zipItem

    development

    ZIPFoundation extends FileManager with zipItem(at:to:). You can zip a single file or an entire directory.

    Key Options:

    • Compression: By default, archives are created without compression. To enable compression, set the compressionMethod parameter to .deflate.
    • Directory Structure: When zipping a directory, a root entry named after the lastPathComponent of the sourceURL is added by default. To prevent this and avoid a parent directory wrapper in the archive, set shouldKeepParent: false.
    let fileManager = FileManager()
    let currentWorkingPath = fileManager.currentDirectoryPath
    var sourceURL = URL(fileURLWithPath: currentWorkingPath)
    sourceURL.appendPathComponent("file.txt")
    var destinationURL = URL(fileURLWithPath: currentWorkingPath)
    destinationURL.appendPathComponent("archive.zip")
    do {
        try fileManager.zipItem(at: sourceURL, to: destinationURL)
    } catch {
        print("Creation of ZIP archive failed with error:\(error)")
    }
  6. Access individual entries in an archive

    development

    You can access specific entries within a ZIP archive without extracting the entire file. Initialize an Archive object with a file URL and an AccessMode.read. The Archive object conforms to Sequence, allowing you to retrieve entries using subscripting with their relative paths. Use the extract(_:to:) method to extract a specific entry to a destination URL.

    let fileManager = FileManager()
    let currentWorkingPath = fileManager.currentDirectoryPath
    var archiveURL = URL(fileURLWithPath: currentWorkingPath)
    archiveURL.appendPathComponent("archive.zip")
    let archive = try Archive(url: archiveURL, accessMode: .read)
    guard let entry = archive["file.txt"] else {
        return
    }
    var destinationURL = URL(fileURLWithPath: currentWorkingPath)
    destinationURL.appendPathComponent("out.txt")
    do {
        try archive.extract(entry, to: destinationURL)
    } catch {
        print("Extracting entry from archive failed with error:\(error)")
    }
  7. Read entry contents using a closure (Consumer)

    development

    You can consume the contents of a ZIP entry without writing them to the file system by using the extract(_:consumer:) method. This method accepts a Consumer closure that is called with chunks of data until the entry is exhausted. You can control the chunk size using the bufferSize parameter.

    try archive.extract(entry, consumer: { (data) in
        print(data.count)
    })
  8. Remove entries from an archive

    development

    To remove an entry, you must first obtain a reference to the entry (e.g., via subscripting) and then pass it to the remove(_:) method. The archive must be opened with .create or .update AccessMode.

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

    development

    To create a new archive, initialize an Archive object with a file URL that does not yet exist and set the accessMode to .create.

    let fileManager = FileManager()
    let currentWorkingPath = fileManager.currentDirectoryPath
    var archiveURL = URL(fileURLWithPath: currentWorkingPath)
    archiveURL.appendPathComponent("newArchive.zip")
    let archive = try Archive(url: archiveURL, accessMode: .create)
  10. Add entries from an in-memory data source (Provider)

    development

    To add an entry from memory, use the addEntry method with a Provider closure. The closure is called repeatedly until the amount of data provided matches the specified uncompressedSize. The closure receives the current position and the requested size to help manage the state of your data source.

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

    development

    To add entries, open the archive with .create or .update AccessMode.

    1. Using a relative path: Use addEntry(with:relativeTo:) by providing the entry's name and a base URL. The name and base URL must combine to form an absolute file URL to the source file.
    2. Using an absolute path: Use addEntry(with:fileURL:) to add files from arbitrary locations that do not share a common base directory.
    let fileManager = FileManager()
    let currentWorkingPath = fileManager.currentDirectoryPath
    var archiveURL = URL(fileURLWithPath: currentWorkingPath)
    archiveURL.appendPathComponent("archive.zip")
    let archive = try Archive(url: archiveURL, accessMode: .update)
    var fileURL = URL(fileURLWithPath: currentWorkingPath)
    fileURL.appendPathComponent("file.txt")
    do {
        try archive.addEntry(with: fileURL.lastPathComponent, relativeTo: fileURL.deletingLastPathComponent())
    } catch {
        print("Adding entry to ZIP archive failed with error:\(error)")
    }
  12. Unzip archives using FileManager.unzipItem

    development

    Use FileManager.unzipItem(at:to:) to recursively extract all entries within a ZIP archive to a destination URL. It is recommended to ensure the destination directory exists before unzipping.

    let fileManager = FileManager()
    let currentWorkingPath = fileManager.currentDirectoryPath
    var sourceURL = URL(fileURLWithPath: currentWorkingPath)
    sourceURL.appendPathComponent("archive.zip")
    var destinationURL = URL(fileURLWithPath: currentWorkingPath)
    destinationURL.appendPathComponent("directory")
    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)")
    }