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

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

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

Начать курс

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

Сжатие и распаковка данных — одна из ключевых задач при разработке приложений под iOS и macOS. ZIP-формат остаётся самым популярным способом упаковки файлов благодаря универсальности и встроенной поддержке в операционных системах Apple. В этой статье мы рассмотрим два основных подхода к работе с ZIP-архивами в Swift: использование встроенного фреймворка Foundation и мощной сторонней библиотеки Zip Foundation.

1. Встроенные средства Foundation (NSFileCoordinator + libz)

Начиная с iOS 9 и macOS 10.11, Apple предоставляет ограниченную, но достаточную для базовых задач поддержку ZIP через класс NSFileCoordinator и системную библиотеку libz. Однако этот способ требует ручного управления потоками и не подходит для сложных сценариев.

Пример: распаковка ZIP-архива с помощью libz

import Foundation
import Compression

func unzipFile(at sourceURL: URL, to destinationURL: URL) throws {
    let sourceData = try Data(contentsOf: sourceURL)
    
    // Используем системный декомпрессор
    let decodedData = try sourceData.withUnsafeBytes { (ptr: UnsafeRawBufferPointer) -> Data in
        let sourcePtr = ptr.baseAddress!.assumingMemoryBound(to: UInt8.self)
        let sourceSize = sourceData.count
        
        // Буфер для распакованных данных (обычно больше исходного)
        let bufferSize = sourceSize * 4
        var buffer = Data(count: bufferSize)
        
        let decodedSize = buffer.withUnsafeMutableBytes { (destPtr: UnsafeMutableRawBufferPointer) -> Int in
            let dest = destPtr.baseAddress!.assumingMemoryBound(to: UInt8.self)
            return compression_decode_buffer(dest, bufferSize, sourcePtr, sourceSize, nil, COMPRESSION_ZLIB)
        }
        
        return buffer.prefix(decodedSize)
    }
    
    // Сохраняем распакованные данные
    try decodedData.write(to: destinationURL, options: .atomic)
}

Ограничения встроенного подхода:

  • Нет поддержки многофайловых архивов
  • Отсутствует работа с паролями
  • Требуется ручное управление буферами
  • Сложно обрабатывать большие файлы

2. Библиотека Zip Foundation (рекомендуемый способ)

Zip Foundation — это open-source библиотека, которая предоставляет простой и элегантный API для работы с ZIP-архивами в Swift. Она поддерживает:

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

Установка через Swift Package Manager

// В файле Package.swift или через Xcode:
dependencies: [
    .package(url: "https://github.com/marmelroy/Zip.git", from: "2.1.0")
]

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

import Zip

func createZipArchive() {
    let fileManager = FileManager.default
    let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
    
    // Собираем файлы для архивации
    let file1 = documentsURL.appendingPathComponent("report.pdf")
    let file2 = documentsURL.appendingPathComponent("photo.jpg")
    let filesToZip = [file1, file2]
    
    // Путь к будущему архиву
    let archiveURL = documentsURL.appendingPathComponent("backup.zip")
    
    do {
        // Прогресс через замыкание
        try Zip.zipFiles(paths: filesToZip, zipFilePath: archiveURL, password: nil, progress: { (progress) in
            print("Прогресс архивации: \(Int(progress * 100))%")
        })
        print("Архив создан: \(archiveURL.path)")
    } catch {
        print("Ошибка создания архива: \(error.localizedDescription)")
    }
}

Пример: распаковка архива с паролем

import Zip

func unzipProtectedArchive() {
    let fileManager = FileManager.default
    let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
    
    let archiveURL = documentsURL.appendingPathComponent("secure_backup.zip")
    let destinationURL = documentsURL.appendingPathComponent("UnzippedFiles")
    
    do {
        // Создаём папку назначения
        try fileManager.createDirectory(at: destinationURL, withIntermediateDirectories: true)
        
        // Распаковка с паролем
        try Zip.unzipFile(archiveURL, destination: destinationURL, overwrite: true, password: "secret123", progress: { (progress) in
            DispatchQueue.main.async {
                print("Распаковано: \(Int(progress * 100))%")
            }
        })
        print("Распаковка завершена!")
    } catch {
        print("Ошибка распаковки: \(error.localizedDescription)")
    }                    

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