Working with ZIP/archives in Swift (iOS/macOS)
In modern iOS and macOS development, there is often a need to compress files into ZIP archives or extract data from them. This may be necessary for saving space, transferring data over a network, or organizing backups. In this article, we will look at two main approaches: using the built-in capabilities of Foundation and the popular third-party library ZipFoundation.
Built-in Foundation Tools (NSFileCoordinator + Low-Level Work)
Apple does not provide a high-level API for working with ZIP in Foundation. However, starting with iOS 12 / macOS 10.14, it became possible to use NSFileCoordinator and NSFileWrapper for some operations. But for full-fledged work with ZIP, the system utility libz is most often used via the C interface.
Example: Compressing data with libz
import Compressionimport Foundation
func compressData(_ data: Data) -> Data? { let sourceData = data as NSData let compressedData = NSMutableData(length: data.count)! var compressedSize = data.count let result = compression_encode( compressedData.mutableBytes.assumingMemoryBound(to: UInt8.self), &compressedSize, sourceData.bytes.assumingMemoryBound(to: UInt8.self), data.count, nil, COMPRESSION_ZLIB ) guard result == COMPRESSION_STATUS_OK else { return nil } compressedData.length = compressedSize return compressedData as Data}ZipFoundation Library (Recommended Approach)
ZipFoundation is a lightweight open-source library for working with ZIP archives in Swift. It supports iOS, macOS, tvOS, and watchOS. Installation via Swift Package Manager:
// In Package.swift or via Xcodedependencies: [ .package(url: "https://github.com/weichsel/ZIPFoundation.git", from: "0.9.0")]Key Features of ZipFoundation:
- Creating ZIP archives from files and folders
- Extracting individual files or the entire archive
- Password support (AES-256)
- Streaming support for large files
- Compatibility with macOS and iOS
Example 1: Creating a ZIP Archive
import ZIPFoundationimport Foundation
func createZip(from sourceURL: URL, to destinationURL: URL) throws { let fileManager = FileManager() // Create an archive from a folder try fileManager.zipItem(at: sourceURL, to: destinationURL) // Or from a single file // try fileManager.zipItem(at: sourceURL, to: destinationURL, // shouldKeepParent: false)}
// Usagelet documents = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!let sourceFolder = documents.appendingPathComponent("MyData")let archiveURL = documents.appendingPathComponent("backup.zip")
do { try createZip(from: sourceFolder, to: archiveURL) print("Archive created: \\(archiveURL.path)")} catch { print("Error: \\(error)")}Example 2: Extracting a ZIP Archive
import ZIPFoundation
func unzipArchive(at sourceURL: URL, to destinationURL: URL) throws { let fileManager = FileManager() // Create the destination folder if it doesn't exist try fileManager.createDirectory(at: destinationURL, withIntermediateDirectories: true) // Extract try fileManager.unzipItem(at: sourceURL, to: destinationURL)}
// Usagelet archiveURL = documents.appendingPathComponent("backup.zip")let extractFolder = documents.appendingPathComponent("ExtractedData")
do { try unzipArchive(at: archiveURL, to: extractFolder) print("Extraction complete")} catch {