Работа с файлами в Swift: от открытия до шифрования

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

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

Начать курс

Почему Swift — это не только про UI, но и про файлы

Когда разработчики начинают изучать Swift, они чаще всего пишут приложения с интерфейсами, анимациями и сетевыми запросами. Но рано или поздно возникает задача: сохранить данные на устройстве, прочитать конфиг, распарсить JSON-файл или просто скопировать документ. И тут многие теряются. А зря — работа с файлами в Swift настолько элегантна и продумана, что после неё в другие языки возвращаться не хочется.

В этой статье мы разберём взаимодействие с файловой системой в Swift от простого к сложному. Вы узнаете, как создавать, читать, записывать и удалять файлы, работать с директориями, обрабатывать ошибки и даже шифровать данные. Поехали!

Основы: FileManager и FileHandle

Сердце файловой системы в Swift — класс FileManager. Это «диспетчер», который знает всё о папках и файлах на устройстве. Он позволяет проверять существование, перемещать, копировать и удалять объекты. Для работы с содержимым файлов (чтение и запись) используется FileHandle или более высокоуровневые методы Data, String и Codable.

Где хранить файлы? Пути и директории

Прежде чем писать код, нужно понять, куда мы имеем право сохранять данные. В iOS и macOS есть строгие правила: приложение может писать только в свою песочницу (sandbox). Самые популярные директории:

  • Documents — для пользовательских данных, которые должны сохраняться при бэкапах.
  • Library/Caches — для временных файлов, которые система может удалить.
  • tmp — для одноразовых файлов, удаляется при перезагрузке.

Получить путь к нужной папке проще простого:

import Foundation

let fileManager = FileManager.default
let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
print("Documents: \(documentsURL.path)")

let cachesURL = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first!
print("Caches: \(cachesURL.path)")

Создание и удаление файлов в Swift

Начнём с самого базового — создадим пустой файл в директории Documents:

let fileURL = documentsURL.appendingPathComponent("myFile.txt")

// Создаём файл, если его нет
if !fileManager.fileExists(atPath: fileURL.path) {
    fileManager.createFile(atPath: fileURL.path, contents: nil, attributes: nil)
    print("Файл создан!")
}

Обратите внимание: createFile(atPath:contents:attributes:) возвращает Bool, который сообщает, удалось ли создать файл. Всегда проверяйте результат — это спасёт от неожиданных ошибок.

Удаление файла делается одной строкой:

do {
    try fileManager.removeItem(at: fileURL)
    print("Файл удалён")
} catch {
    print("Ошибка удаления: \(error.localizedDescription)")
}

Здесь мы используем try и блок do-catch — стандартный подход для обработки ошибок в Swift. Никогда не вызывайте removeItem без проверки, иначе приложение упадёт.

Чтение и запись текстовых файлов

Самый частый сценарий — работа с текстовыми данными. Swift предлагает удобные методы для чтения файла Swift и записи через строки:

// Запись текста
let text = "Привет, мир! Hello, Swift!"
do {
    try text.write(to: fileURL, atomically: true, encoding: .utf8)
    print("Текст записан")
} catch {
    print("Ошибка записи: \(error)")
}

// Чтение текста
do {
    let savedText = try String(contentsOf: fileURL, encoding: .utf8)
    print("Прочитано: \(savedText)")
} catch {
    print("Ошибка чтения: \(error)")
}

Флаг atomically: true гарантирует, что запись будет атомарной — сначала данные пишутся во временный файл, потом переименовываются. Это предотвращает повреждение файла при сбоях.

Работа с бинарными данными: изображения, архивы

Иногда нужно сохранить не текст, а массив байт — например, изображение или зашифрованные данные. Для этого используем Data:

// Создаём бинарные данные (например, массив байт)
let binaryData = Data([0x48, 0x65, 0x6C, 0x6C, 0x6F]) // "Hello"

let binaryURL = documentsURL.appendingPathComponent("binary.dat")
do {
    try binaryData.write(to: binaryURL, options: .atomic)
    print("Бинарные данные записаны")
    
    // Чтение обратно
    let loadedData = try Data(contentsOf: binaryURL)
    print("Загружено \(loadedData.count) байт")
} catch {
    print("Ошибка с бинарными данными: \(error)")
}

Для работы с изображениями (UIImage на iOS) нужно конвертировать их в Data через pngData() или jpegData(compressionQuality:).

JSON — король форматов для Swift

Работа с JSON в Swift заслуживает отдельного абзаца. Благодаря протоколу Codable сериализация превращается в магию. Допустим, у нас есть структура пользователя:

struct User: Codable {
    let name: String
    let age: Int
    let isActive: Bool
}

let users = [
    User(name: "Анна", age: 28, isActive: true),
    User(name: "Борис", age: 35, isActive: false)
]

let jsonURL = documentsURL.appendingPathComponent("users.json")
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted

do {
    let jsonData = try encoder.encode(users)
    try jsonData.write(to: jsonURL)
    print("JSON записан")
    
    // Чтение JSON
    let loadedData = try Data(contentsOf: jsonURL)
    let decoder = JSONDecoder()
    let loadedUsers = try decoder.decode([User].self, from: loadedData)
    print("Загружено \(loadedUsers.count) пользователей")
    print(loadedUsers[0].name) // Анна
} catch {
    print("Ошибка работы с JSON: \(error)")
}

Обратите внимание: JSONEncoder автоматически конвертирует поля camelCase в snake_case, если задать keyEncodingStrategy. Это невероятно удобно при работе с серверными API.

Управление директориями: создаём свою файловую структуру

Часто нужно организовать файлы по папкам. Swift FileManager позволяет создавать вложенные директории одной командой:

let imagesDir = documentsURL.appendingPathComponent("Images")
let subDir = imagesDir.appendingPathComponent("2024")

do {
    // Создаём все недостающие папки по пути
    try fileManager.createDirectory(at: subDir, withIntermediateDirectories: true, attributes: nil)
    print("Директория создана: \(subDir.path)")
    
    // Пол                

Блоги

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