Работа с файловой системой в Swift: практическое руководство для iOS/macOS

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

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

Начать курс

Введение: Почему работа с файловой системой в Swift — это важно

Любое приложение на iOS или macOS рано или поздно сталкивается с необходимостью сохранять данные на устройстве пользователя. Будь то кэширование изображений, загрузка документов или сохранение настроек — работа с файловой системой в Swift является фундаментальным навыком для каждого разработчика. В этой статье мы на практике разберем, как использовать FileManager, работать с директориями, читать и записывать файлы, а также управлять их атрибутами. Вы получите готовые примеры кода, которые можно сразу применить в вашем проекте.

Основные инструменты: FileManager и URL

Центральным классом для работы с файловой системой в Swift является FileManager. Он предоставляет единый интерфейс для взаимодействия с файловой системой как на iOS, так и на macOS. Второй ключевой компонент — это URL, который используется для указания путей к файлам и директориям.

Получение стандартных директорий

Самое первое, что нужно уметь — получать пути к системным директориям: Documents, Library, Caches и Temporary. Вот как это делается:

import Foundation

let fileManager = FileManager.default

// Директория Documents — для пользовательских данных
guard let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else {
    fatalError("Не удалось получить Documents директорию")
}
print("Documents: \(documentsURL.path)")

// Директория Caches — для временных кэшируемых данных
let cachesURL = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first!
print("Caches: \(cachesURL.path)")

// Временная директория — для файлов, которые можно удалить в любой момент
let tempURL = fileManager.temporaryDirectory
print("Temp: \(tempURL.path)")

Важно: На iOS приложение может записывать данные только в свою песочницу (sandbox). Доступ к системным файлам или файлам других приложений запрещен.

Создание, чтение и запись файлов

Теперь перейдем к практическим операциям. Рассмотрим, как создавать файлы, записывать в них данные и читать их обратно.

Запись данных в файл

Самый простой способ — использовать метод write(to:) для строк или Data. Но для надежности всегда используйте обработку ошибок:

let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = documentsURL.appendingPathComponent("example.txt")

let content = "Hello, Swift! Это тестовый файл."

do {
    try content.write(to: fileURL, atomically: true, encoding: .utf8)
    print("Файл успешно создан: \(fileURL.lastPathComponent)")
} catch {
    print("Ошибка записи: \(error.localizedDescription)")
}

Чтение данных из файла

Чтение выполняется аналогично — с помощью конструктора String(contentsOf:) или методов Data:

do {
    let savedContent = try String(contentsOf: fileURL, encoding: .utf8)
    print("Содержимое файла: \(savedContent)")
} catch {
    print("Ошибка чтения: \(error.localizedDescription)")
}

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

Для бинарных файлов используйте тип Data:

// Запись бинарных данных
let imageData = UIImage(named: "example.png")?.pngData()
let imageURL = documentsURL.appendingPathComponent("image.png")

do {
    try imageData?.write(to: imageURL)
} catch {
    print("Ошибка записи изображения: \(error)")
}

// Чтение бинарных данных
if let loadedData = try? Data(contentsOf: imageURL) {
    let loadedImage = UIImage(data: loadedData)
    print("Изображение загружено: \(loadedImage != nil)")
}

Управление директориями и файлами

Одной записи и чтения недостаточно. Часто требуется создавать поддиректории, перемещать, копировать или удалять файлы. Рассмотрим основные операции.

Создание директорий

Для создания папок используйте метод createDirectory(at:withIntermediateDirectories:). Параметр withIntermediateDirectories позволяет создать все недостающие родительские директории автоматически:

let newFolderURL = documentsURL.appendingPathComponent("MyFolder/Subfolder")

do {
    try fileManager.createDirectory(at: newFolderURL, withIntermediateDirectories: true, attributes: nil)
    print("Директория создана: \(newFolderURL.path)")
} catch {
    print("Ошибка создания директории: \(error)")
}

Перемещение и копирование файлов

Перемещение и копирование выполняются методами moveItem(at:to:) и copyItem(at:to:):

let sourceURL = documentsURL.appendingPathComponent("example.txt")
let destinationURL = newFolderURL.appendingPathComponent("moved_example.txt")

// Перемещение
do {
    try fileManager.moveItem(at: sourceURL, to: destinationURL)
    print("Файл перемещен в: \(destinationURL.path)")
} catch {
    print("Ошибка перемещения: \(error)")
}

// Копирование
let copyURL = documentsURL.appendingPathComponent("copy_example.txt")
do {
    try fileManager.copyItem(at: destinationURL, to: copyURL)
    print("Файл скопирован: \(copyURL.path)")
} catch {
    print("Ошибка копирования: \(error)")
}

Удаление файлов и проверка существования

Перед удалением всегда проверяйте, существует ли файл, чтобы избежать ошибок:

let fileToDelete = documentsURL.appendingPathComponent("copy_example.txt")

if fileManager.fileExists(atPath: fileToDelete.path) {
    do {
        try fileManager.removeItem(at: fileToDelete)
        print("Файл удален")
    } catch {
        print("Ошибка удаления: \(error)")
    }
} else {
    print("Файл не существует")
}

Работа с атрибутами файлов и перечисление содержимого

Иногда нужно получить информацию о файле: размер, дата создания, тип. Для этого используется метод attributesOfItem(atPath:).

Получение атрибутов файла

let testFileURL = documentsURL.appendingPathComponent("example.txt")

do {
    let attributes = try fileManager.attributesOfItem(atPath: testFileURL.path)
    if let fileSize = attributes[.size] as? NSNumber {
        print("Размер файла: \(fileSize.intValue) байт")
    }
    if let creationDate = attributes[.creationDate] as? Date {
        print("Дата создания: \(creationDate)")
    }
    if let modificationDate = attributes[.modificationDate] as? Date {
        print("Дата изменения: \(modificationDate)")
    }                

Блоги

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