Работа с ZIP/архивами в Swift (iOS/macOS): полное руководство

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Работа с ZIP/архивами в Swift (iOS/macOS)

В современной разработке под iOS и macOS часто возникает необходимость сжимать файлы в ZIP-архивы или извлекать данные из них. Это может понадобиться для экономии места, передачи данных по сети или организации бэкапов. В этой статье мы рассмотрим два основных подхода: использование встроенных возможностей Foundation и популярной сторонней библиотеки ZipFoundation.

Встроенные средства Foundation (NSFileCoordinator + низкоуровневая работа)

Apple не предоставляет высокоуровневого API для работы с ZIP в Foundation. Однако, начиная с iOS 12 / macOS 10.14, появилась возможность использовать NSFileCoordinator и NSFileWrapper для некоторых операций. Но для полноценной работы с ZIP чаще всего используют системную утилиту libz через C-интерфейс.

Пример: сжатие данных с помощью libz

import Compression
import 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 (рекомендуемый способ)

ZipFoundation — это легковесная библиотека с открытым исходным кодом для работы с ZIP-архивами на Swift. Она поддерживает iOS, macOS, tvOS и watchOS. Установка через Swift Package Manager:

// В Package.swift или через Xcode
dependencies: [
    .package(url: "https://github.com/weichsel/ZIPFoundation.git", from: "0.9.0")
]

Основные возможности ZipFoundation:

  • Создание ZIP-архивов из файлов и папок
  • Извлечение отдельных файлов или всего архива
  • Поддержка паролей (AES-256)
  • Работа с потоками для больших файлов
  • Совместимость с macOS и iOS

Пример 1: Создание ZIP-архива

import ZIPFoundation
import Foundation

func createZip(from sourceURL: URL, to destinationURL: URL) throws {
    let fileManager = FileManager()
    
    // Создаём архив из папки
    try fileManager.zipItem(at: sourceURL, to: destinationURL)
    
    // Или из одного файла
    // try fileManager.zipItem(at: sourceURL, to: destinationURL, 
    //     shouldKeepParent: false)
}

// Использование
let 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("Архив создан: \(archiveURL.path)")
} catch {
    print("Ошибка: \(error)")
}

Пример 2: Распаковка ZIP-архива

import ZIPFoundation

func unzipArchive(at sourceURL: URL, to destinationURL: URL) throws {
    let fileManager = FileManager()
    
    // Создаём папку назначения, если её нет
    try fileManager.createDirectory(at: destinationURL, 
                                     withIntermediateDirectories: true)
    
    // Распаковываем
    try fileManager.unzipItem(at: sourceURL, to: destinationURL)
}

// Использование
let archiveURL = documents.appendingPathComponent("backup.zip")
let extractFolder = documents.appendingPathComponent("ExtractedData")

do {
    try unzipArchive(at: archiveURL, to: extractFolder)
    print("Распаковка завершена")
} catch {
    print("Ошибка распаковки: \(error)")
}

Пример 3: Работа с отдельными файлами в архиве

import ZIPFoundation

func listFilesInArchive(at archiveURL: URL) throws -> [String] {
    guard let archive = Archive(url: archiveURL, accessMode: .read) else {
        throw NSError(domain: "ZIPError", code: -1, 
                      userInfo: [NSLocalizedDescriptionKey: "Не удалось открыть архив"])
    }
    
    var fileNames: [String] = []
    for entry in archive {
        fileNames.append(entry.path)
        print("Файл: \(entry.path), размер: \(entry.uncompressedSize) байт")
    }
    return fileNames
}

// Извлечение конкретного файла
func extractSpecificFile(from archiveURL: URL, 
                         fileName: String, 
                         to destinationURL: URL) throws {
    guard let archive = Archive(url: archiveURL, accessMode: .read) else {
        throw NSError(domain: "ZIPError", code: -1, 
                      userInfo: nil)
    }
    
    guard let entry = archive[fileName] else {
        throw NSError(domain: "ZIPError", code: -2,                    

Рекомендаций