Следующая статья поможет вам: Работа с файлами на iOS с помощью Swift
Вернуться в блог
Вы используете FileManager для работы с файлами и каталогами на iOS. Это Swift API, который помогает вам читать и записывать различные форматы данных и файлов. В этом руководстве по разработке приложений вы узнаете, как работать с файлами на iOS с помощью Swift.
Вот что мы обсудим:
- Как работать с файлами и каталогами с помощью FileManager
- Чтение и запись в текстовые файлы, списки, изображения и JSON.
- Как записать строки в файл и прочитать их обратно
- Получение доступа к каталогам, к которым имеет доступ ваше iOS-приложение
- Работа с типом данных Swift и многое другое
Компонент FileManager, предназначенный для разработки iOS, представляет собой интерфейс к файловой системе iPhone. Вы используете его для чтения и записи файлов в своем приложении iOS.
Короче говоря, вы используете FileManager, чтобы получить путь к файлам на iPhone, к которым имеет доступ ваше приложение iOS. Затем вы можете использовать этот путь для чтения файла или записи в него, в зависимости от потребностей вашего приложения. Вы также можете использовать FileManager для работы с каталогами.
Работа с FileManager при создании приложения для iOS может быть немного нелогичной. Если вы раньше работали с файлами, например, на своем Mac, вы хорошо знаете, как файлы организованы в файловой системе, состоящей из иерархических каталогов. Вы можете получить доступ к любому файлу по его пути, например /home/reinder/Documents/todo-list.txt, при условии, что у вас есть разрешение на доступ к этому файлу.
Работа с файлами в iOS отличается несколькими способами:
- Приложения на iOS ограничены «песочницей», что означает, что у них нет доступа к системным файлам и ресурсам. Это мера безопасности.
- У файлов в iOS есть путь, но обычно вы работаете только с объектом URL, содержащим этот путь. Вы редко работаете с путями напрямую.
- Обычно вы читаете/записываете файлы приложения в нескольких назначенных каталогах, например в каталоге документов вашего приложения или из пакета приложения.
В самой iOS нет такого файлового менеджера, как Finder, за исключением приложения «Файлы», которое дает вам доступ к файлам в iCloud. iOS — закрытая система, что важно иметь в виду.
Компонент FileManager служит оболочкой поверх файловой системы. Вы используете его для получения ссылки на файл. Вот пример:
let path = FileManager.default.urls (для: .documentDirectory, в: .userDomainMask)[0].appendingPathComponent(“todos.txt”)
Фанки, правда? Мы создали только ссылку на файл todos.txt в каталоге документов приложения. Сам файл сохраняется на iPhone по адресу:
файл:///var/mobile/Containers/Data/Application//Documents/todos.txt
Давайте погрузимся и посмотрим, как это работает!
Работаете с iPhone Simulator? Выполните print(path.absoluteString), а затем $ open path в терминале, чтобы открыть файл или папку по указанному пути. Имейте в виду, что каталог (на вашем Mac), в котором хранятся файлы симулятора, может меняться при запуске вашего приложения. Содержимое Симулятора скопировано, поэтому вы можете увидеть устаревший файл (и задаться вопросом, почему)…
Чтение файла с помощью Swift на iOS требует двухэтапного подхода:
- Получить ссылку на файл
- Прочтите файл
Представьте, что мы сохранили несколько элементов списка дел в файле todos.txt, разделенном запятыми. Как мы можем прочитать этот файл с диска и что-то сделать с его содержимым?
1. Получите файловый менеджер по умолчанию.
Первый шаг — получить ссылку на компонент FileManager по умолчанию, например: FileManager.default . Это общий компонент iOS, уникальный для процесса нашего приложения.
Мы не можем получить какой-либо произвольный файл из iOS, поэтому нам нужно использовать отправную точку: каталог документов. Здесь вы храните пользовательские документы, то есть файлы, которые хочет сохранить пользователь вашего приложения.
2. Получить каталог документов
Мы собираемся использовать функцию urls(for:in:) FileManager, чтобы получить ссылку на каталог документов приложения. Так:
FileManager.default.urls(для: .documentDirectory, в: .userDomainMask)[0]
URL-адреса (for:in:) предназначены для возврата общих каталогов файловой системы, таких как каталоги документов и временных файлов. .documentDirectory — это значение перечисления из FileManager.SearchPathDirectory, которое похоже на жестко закодированный список часто используемых вами каталогов iOS. .userDomainMask дополнительно указывает, где искать запрошенный каталог.
Приведенный выше вызов функции urls(for:in:) возвращает массив объектов URL. Фактически, для обычного вызова каталога документов он просто возвращает массив с одним объектом URL. Мы получаем это через индекс [0].
При работе с файлами в iOS вы так часто используете каталог документов, что имеет смысл создать для него вспомогательную функцию. Так:
func getDocumentDirectory() -> URL {
вернуть FileManager.default.urls (для: .documentDirectory, в: .userDomainMask)[0]
}
В зависимости от предпочитаемой вами архитектуры вы можете даже создать в расширении простую функцию:
расширение FileManager {
func documentDirectory() -> URL {
вернуть self.urls(для: .documentDirectory, в: .userDomainMask)[0]
}
}
Полезно знать: файлы, хранящиеся в каталоге документов приложения, также копируются в iCloud при резервном копировании этого приложения. Используйте его только для файлов, которые пользователь хотел бы сохранить. Вы можете исключить файлы из резервного копирования, пометив их дополнительным флагом файла.
3. Добавить компонент пути
На этом этапе URL-адреса (for: , in: )[0] код содержит URL-адрес каталога документов со следующим путем:
файл:///var/mobile/Containers/Data/Application//Documents/
Следующим шагом является добавление фактического файла, из которого мы хотим прочитать, с именем todos.txt. Вы можете сделать это с помощью функции AppendPathComponent() объекта URL. Так:
let path = FileManager.default.urls (для: .documentDirectory, в: .userDomainMask)[0].appendingPathComponent(“todos.txt”)
Приведенный выше код добавляет имя файла к уже имевшемуся у нас пути. Если бы путь к файлу, с которым мы работаем, был реальным жестко запрограммированным строковым путем, мы бы просто «добавили» имя файла после последней косой черты.
На данный момент вот что мы получили:
файл:///var/mobile/Containers/Data/Application//Documents/todos.txt
Это путь к файлу, который мы можем прочитать и получить данные из файла. Хорошей новостью является то, что мы все еще работаем с объектами Swift, такими как URL, в пользу простых путей к файлам. Плохая новость заключается в том, что этот код получения пути к файлу выглядит ужасно…
Вы можете узнать концепцию URL-адресов при просмотре веб-страниц, но вы также можете использовать стандарт URL-адресов для идентификации файлов на компьютере. При этом к URL-адресам будет добавлена схема file://, а не https:// для Интернета.
4. Чтение из файла
Хорошо, давайте прочитаем эти задачи! Имея путь, прочитать содержимое файла легко:
let todos = попробуйте String(contentsOf:path)
Видите там попытку? Это означает, что нам придется обрабатывать ошибки, например:
делать {
let todos = попробуйте String(contentsOf:path)
для задачи в todos.split(разделитель: «;») {
распечатать(задача)
}
} ловить {
печать (error.localizedDescription)
}
Учитывая todos.txt с текстом Помыть посуду;Приготовить ужин;Выгулять домашнюю ящерицу, приведенный выше код выводит:
Помыть посуду
Приготовить ужин
Выгуливая домашнюю ящерицу
Чтобы прочитать файл в Swift, вы можете напрямую использовать тип String. В приведенном выше коде мы используем инициализатор String(contentsOf:) для чтения из файла по заданному URL-адресу.
В качестве альтернативы вы также можете использовать инициализатор Data(contentsOf:). Вместо чтения строки это будет напрямую читать байты из файла. Затем вы можете работать с этими байтами дальше, например, создавая представление изображения.
Потрясающий! Перейдем к записи в файлы.
Давайте посмотрим, как можно записать в файл в Swift. Как и раньше, вы используете двухэтапный подход:
- Получить ссылку на файл
- Записать в файл
В предыдущем разделе мы обсудили, как получить ссылку на файл с помощью FileManager, чтобы получить URL-адрес этого файла. Так:
let path = FileManager.default.urls (для: .documentDirectory, в: .userDomainMask)[0].appendingPathComponent(“todos.txt”)
Если у вас есть строка todos с несколькими элементами задач, вы можете записать их в todos.txt следующим образом:
let todos = «Достичь мирового господства;Есть кошачью еду;Спать»
let path = FileManager.default.urls (для: .documentDirectory, в: .userDomainMask)[0].appendingPathComponent(“todos.txt”)
делать {
попробуйте todos.write(to: путь, атомарно: true, кодировка: .utf8)
} ловить {
печать (error.localizedDescription)
}
В приведенном выше коде у нас есть дескриптор пути и задач. Затем вы вызываете функцию write() для строки, чтобы напрямую записать ее в файл.
Несколько примечаний:
- Параметр атомарности (и концепцию) стоит запомнить. Запись в файл атомарно означает, что файл сначала записывается во временный файл, который затем переименовывается в путь. Это гарантирует, что файл не будет поврежден, если ваше приложение выйдет из строя во время операции записи.
- Вам необходимо указать кодировку, которая будет использоваться для записи строки в файл. Безопасный выбор здесь — .utf8. Когда вы читаете из того же файла, обязательно выберите кодировку UTF-8 (т. е. ту же самую). Подробнее о кодировании здесь.
- Как и раньше, мы используем do-try-catch для обработки ошибок, которые могут возникнуть во время записи в файл. Также полезно знать, что файлы, которые еще не существуют, будут созданы, а существующие файлы будут перезаписаны.
Приложения iOS распространяются через так называемый пакет приложений, который содержит двоичный файл приложения и любые ресурсы, которые вы распространяете вместе с вашим приложением, например значок приложения.
Когда вы добавляете файл в проект приложения в Xcode, он добавляется в пакет приложения. Теперь, когда мы рассмотрели чтение/запись произвольных файлов в iOS, как вы читаете файлы в пакете приложения?
Подход тот же, что и раньше:
- Получить ссылку на файл в пакете приложения
- Прочтите файл
Сначала вам нужно получить ссылку на файл в комплекте приложения. Отправной точкой здесь является Bundle.main, а не FileManager. Вот как вы можете получить путь к файлу:
пусть путь = Bundle.main.url(forResource: «todos», withExtension: «txt»)
С помощью приведенного выше кода мы создаем постоянный путь типа URL, который содержит ссылку на файл todos.txt в пакете приложения. Как и раньше, теперь вы можете читать данные из этого файла. Note этот путь является необязательным — в случае, если файл не существует — поэтому вам нужно его развернуть.
Несколько вещей, которые стоит отметить:
- Вышеупомянутый подход, очевидно, работает только для файлов, которые вы добавили в пакет приложения, то есть для файлов, добавленных в ваш проект в Xcode. Для ресурсов, которые вы добавили в файл .xcassets, вы можете использовать общий подход, т.е. Image(named:).
- В отличие от предыдущего, вы не добавляете путь к файлу, например, к каталогу документов. Вместо этого вы определяете имя и расширение файла с помощью url(forResource:withExtension:). Синтаксис немного многословен и затрудняет работу с файловой системой, что может быть преимуществом, поскольку способствует ясности.
- Вы не можете записывать или изменять файлы в пакете приложений. Это мера безопасности — в противном случае вы сможете, например, небезопасно переписать или исправить двоичный файл приложения. Песочница – это хорошо!
Давайте двигаться дальше!
До сих пор мы рассматривали только работу с отдельными файлами, имея в виду, что вам нужно получить один конкретный файл из пакета приложения. Что делать, если вы хотите организовать больше файлов в каталогах?
Несколько распространенных сценариев включают в себя…
- … организация фотографий и записей в определенных подкаталогах
- … организация по датированным каталогам для упрощения экспорта
- … создание подпапок просто потому, что вам так хочется
Вот как вы создаете новый каталог с помощью FileManager:
let path = FileManager.default.urls (для: .documentDirectory, в: .userDomainMask)[0].appendingPathComponent(“photosFolder”)
если !FileManager.default.fileExists(atPath: path.absoluteString) {
пытаться! FileManager.default.createDirectory(at: путь, withIntermediateDirectories: true, атрибуты: ноль)
}
Ого! Здесь происходит несколько вещей:
- Сначала мы задаем путь к каталогу, который хотим создать, — папке с именем /photosFolder. Мы подготавливаем путь перед созданием каталога по этому пути.
- Затем мы проверяем, существует ли файл или каталог по указанному пути. Это сделано для того, чтобы мы не пытались создать каталог по уже занятому пути.
- Наконец, мы создаем новый каталог по адресу path.
Несколько вещей, которые стоит отметить:
- В приведенном выше примере кода мы отключили ошибки метода createDirectory() с помощью try!. В вашем собственном коде вам нужно будет заключить эту строку в блок do-catch и правильно обработать ошибки.
- Note что на момент let path = определяемый нами путь еще не существует! Там ничего нет. Путь не является файловой системой. Это просто место в файловой системе, и мы не знаем, что там находится, пока не прочитаем из него (мета)данные.
- Файлы не обязательно должны иметь расширение, поэтому вполне возможно, что photosFolder представляет собой текстовый файл без расширения. Вот почему мы проверяем, существует ли файл или папка по указанному пути. (То же самое касается каталогов, заканчивающихся на .txt…)
- Если для параметра withIntermediateDirectories установлено значение true, функция создаст все несуществующие каталоги «до» тех, которые находятся на вашем пути. Итак, если ваш путь — photos/2020/08 и ни один из этих каталогов еще не существует, они будут созданы за один раз.
- Path.absoluteString — это строковое представление константы пути типа URL. Это «абсолютный» путь в том смысле, что это абсолютный путь, начинающийся в корне файловой системы. Некоторые API в FileManager используют строки, другие — URL. Хорошо знать!
Быстрый Note: Каталоги и «папки» — это одно и то же. Технически любой каталог является подкаталогом, если только этот каталог не является корнем системы /.
Пффф! Вы уже начинаете испытывать благодарность за такие API, как UIImage(название: «cats»)? Если вам придется считывать байты из файловой системы напрямую для каждого изображения, которое вы хотите отобразить на экране, ваш код довольно быстро запутается. Работа с FileManager не самая приятная вещь, и это нормально.
Прежде чем мы прекратим работу, давайте рассмотрим несколько советов и приемов.
В приведенных ниже примерах кода константа document — это путь к каталогу документов приложения. Он устанавливается с помощью let document = FileManager.default.urls (для: .documentDirectory, в: .userDomainMask).[0].
Чтение/запись файла JSON
Учитывая следующий файл JSON:
{
«ID пользователя»: 99,
«имя»: «Зафод»,
«Вход в систему»: ложь
}
Как читать и анализировать файл JSON с помощью Swift?
пусть путь = document.appendingPathComponent(“config.json”)
делать {
пусть данные = попробуйте данные (contentsOf: путь)
let json = попробуйте JSONSerialization.jsonObject(с: данными, опциями: [])
если пусть root = json как? [String: Any],
пусть имя = корень[“name”] как? Нить {
распечатать (имя)
}
} ловить {
печать (error.localizedDescription)
}
Как записать JSON обратно в файл?
let json = #”{“loggedIn”: false, “name”: “Zaphod”}”#
делать {
попробуйте json.write(to: путь, атомарно: true, кодировка: .utf8)
} ловить {
печать (error.localizedDescription)
}
Большинство библиотек, работающих с JSON, имеют API для возврата JSON в виде объекта Swift Data, который вы также можете записать на диск, как обсуждалось. Имейте в виду, что Codable и SwiftyJSON — более простые альтернативы JSONSerialization.
Видите эту строку в приведенном выше примере кода? Это необработанная строка, заключенная между символами #. Когда вы кодируете такой строковый литерал, вам не нужно экранировать двойные кавычки», что полезно при работе со строками JSON.
Чтение/запись файла Plist
Допустим, у вас есть файл .plist, встроенный в проект вашего приложения. Как вы это читаете?
если пусть путь = Bundle.main.path(forResource: name, ofType: «plist»),
пусть xml = FileManager.default.contents(atPath: путь)
{
если дай фрукты = попробуй? PropertyListSerialization.propertyList(из: xml, параметры: .mutableContainersAndLeaves, формат: ноль)) как? [String] {
печать(фрукты)
}
}
Вы можете узнать больше о списках в этом уроке: Как: Работа с Plist в Swift
Чтение/запись изображения или файла данных
А как насчет изображений? Если вы не можете положиться на старый добрый UIImage(named:) или Image(named:), как перейти от битов к пикселям? Вот как вы можете читать файл изображения и писать в него.
пусть данные = Данные()
imageView.image = UIImage(данные: данные)
Вы можете получить данные из различных источников, например, загрузив изображение по сети. Имейте в виду, что приведенный выше инициализатор UIImage(data:) не зависит от масштаба. Если вы хотите отображать изображения в определенном масштабе 1x, 2x или 3x, используйте UIImage(data: , Scale: 2.0) или UIImage(data: , Scale: UIScreen.main.scale).
На работу с изображениями и файлами в Swift влияет формат файла изображения, например JPEG или PNG. Вы можете преобразовать объекты UIImage в объекты Data, представленные в различных форматах файлов. Для объекта изображения типа UIImage функции image.pngData() и image.jpgData() возвращают объекты данных, которые можно сохранить в файл .png или .jpg.
Чтение/запись с помощью NSKeyedArchiver
Компонент NSKeyedArchiver очень полезен для создания простых хранилищ данных. Вы можете создавать свои собственные классы объектов Swift и сохранять их в двоичный файл, и наоборот.
Выполнив несколько предварительных условий, вы можете преобразовать объект, совместимый с NSCoding, в хранилище данных с помощью:
let data = попробуйте NSKeyedArchiver.archivedData(withRootObject:, requireSecureCoding: false)
попробуйте data.write(to: путь,)
Вы можете прочитать тот же файл данных обратно с помощью NSKeyedUnarchiver, вот так:
пусть данные = попробуйте данные (contentsOf: путь)
let todos = попробуйте NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(данные)
Узнайте больше о работе с NSKeyedArchiver в этом руководстве: Хранение данных с помощью NSCoding и NSKeyedArchiver.
Чтение/запись текстового файла
Мы уже обсуждали это раньше, но вот несколько фрагментов для работы со скромными текстовыми файлами. Вот как вы читаете текстовый файл с помощью Swift:
пусть путь =
делать {
let todos = попробуйте String(contentsOf:path)
распечатать (todos)
} ловить {
печать (error.localizedDescription)
}
Вот как можно записать строку в текстовый файл:
let text = «Привет, мир!»
пусть путь =
делать {
text.write(to: путь, атомарно: true, кодировка: .utf8)
} ловить {
печать (error.localizedDescription)
}
В этом уроке мы рассмотрели, как можно работать с файлами и каталогами на iOS с помощью Swift. Вы научились читать и записывать файлы различных форматов. Мы обсудили работу с данными, изображениями, текстовыми файлами, JSON, списками и многим другим. Потрясающий!