Доступ к файловой системе в Xamarin.iOS

Вы можете использовать Xamarin.iOS и System.IO классы в библиотеке базовых классов .NET (BCL) для доступа к файловой системе iOS. Класс File позволяет создавать, удалять и считывать файлы, а класс и Directory — создавать, удалять или перечислять содержимое каталогов. Также можно использовать Stream подклассы, которые могут обеспечить большую степень контроля над операциями с файлами (например, сжатием или поиском позиции в файле).

IOS накладывает некоторые ограничения на то, что приложение может сделать с файловой системой, чтобы сохранить безопасность данных приложения и защитить пользователей от злокачественного приложения. Эти ограничения являются частью песочницы приложения — набор правил, ограничивающих доступ приложения к файлам, предпочтениям, сетевым ресурсам, оборудованию и т. д. Приложение ограничено чтением и записью файлов в своем домашнем каталоге (установленное расположение); он не может получить доступ к файлам другого приложения.

IOS также имеет некоторые функции файловой системы: некоторые каталоги требуют специального лечения в отношении резервных копий и обновлений, а приложения также могут совместно использовать файлы друг с другом и приложение "Файлы " (с iOS 11) и через iTunes.

В этой статье рассматриваются функции и ограничения файловой системы iOS, а также пример приложения, демонстрирующего использование Xamarin.iOS для выполнения некоторых простых операций файловой системы:

Пример iOS, выполняющий некоторые простые операции файловой системы

Общий доступ к файлам

Xamarin.iOS позволяет использовать классы .NET System.IO для операций файловой системы в iOS.

Приведенные ниже фрагменты кода иллюстрируют некоторые распространенные операции с файлами. Они находятся ниже в файле SampleCode.cs в примере приложения для этой статьи.

Работа с каталогами

Этот код перечисляет вложенные каталоги в текущем каталоге (указанный параметром "./), который является расположением исполняемого файла приложения. Выходные данные будут списком всех файлов и папок, развернутых с приложением (отображается в окне консоли во время отладки).

var directories = Directory.EnumerateDirectories("./");
foreach (var directory in directories) {
      Console.WriteLine(directory);
}

Чтение файлов

Для чтения текстового файла требуется только одна строка кода. В этом примере будет отображаться содержимое текстового файла в окне вывода приложения.

var text = File.ReadAllText("TestData/ReadMe.txt");
Console.WriteLine(text);

сериализация XML

Хотя работа с полным System.Xml пространством имен выходит за рамки этой статьи, вы можете легко десериализировать XML-документ из файловой системы с помощью StreamReader, как этот фрагмент кода:

using (TextReader reader = new StreamReader("./TestData/test.xml")) {
      XmlSerializer serializer = new XmlSerializer(typeof(MyObject));
      var xml = (MyObject)serializer.Deserialize(reader);
}

Дополнительные сведения см. в документации по System.Xml и сериализации. См. документацию по компоновщику Xamarin.iOS. Часто необходимо добавить [Preserve] атрибут в классы, которые планируется сериализовать.

Создание файлов и каталогов

В этом примере показано, как использовать Environment класс для доступа к папке Documents, где можно создавать файлы и каталоги.

var documents =
 Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var filename = Path.Combine (documents, "Write.txt");
File.WriteAllText(filename, "Write this text into a file");

Создание каталога является аналогичным процессом:

var documents =
 Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var directoryname = Path.Combine (documents, "NewDirectory");
Directory.CreateDirectory(directoryname);

Дополнительные сведения см. в справочнике по API System.IO.

Сериализация JSON

Json.NET — это высокопроизводительная платформа JSON, которая работает с Xamarin.iOS и доступна в NuGet. Добавьте пакет NuGet в проект приложения с помощью Add NuGet в Visual Studio для Mac:

Добавление пакета NuGet в проект приложений

Затем добавьте класс, который будет выступать в качестве модели данных для сериализации или десериализации (в данном случае Account.cs):

using System;
using System.Collections.Generic;
using Foundation; // for Preserve attribute, which helps serialization with Linking enabled

namespace FileSystem
{
    [Preserve]
    public class Account
    {
        public string Email { get; set; }
        public bool Active { get; set; }
        public DateTime CreatedDate { get; set; }
        public List<string> Roles { get; set; }

        public Account() {
        }
    }
}

Наконец, создайте экземпляр Account класса, сериализируйте его в данные JSON и запишите его в файл:

// Create a new record
var account = new Account(){
    Email = "monkey@xamarin.com",
    Active = true,
    CreatedDate = new DateTime(2015, 5, 27, 0, 0, 0, DateTimeKind.Utc),
    Roles = new List<string> {"User", "Admin"}
};

// Serialize object
var json = JsonConvert.SerializeObject(account, Newtonsoft.Json.Formatting.Indented);

// Save to file
var documents = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var filename = Path.Combine (documents, "account.json");
File.WriteAllText(filename, json);

Дополнительные сведения о работе с данными JSON в приложении .NET см. в документации json.NET.

Примечания

Несмотря на сходство между операциями файлов Xamarin.iOS и .NET, iOS и Xamarin.iOS отличаются от .NET некоторыми важными способами.

Предоставление доступа к файлам проекта во время выполнения

По умолчанию при добавлении файла в проект он не будет включен в окончательную сборку и поэтому не будет доступен приложению. Чтобы включить файл в сборку, необходимо пометить его специальным действием сборки с именем Content.

Чтобы пометить файл для включения, щелкните правой кнопкой мыши файлы и выберите "Создать содержимое действия>" в Visual Studio для Mac. Вы также можете изменить действие сборки на листе свойств файла.

Учет регистра

Важно понимать, что файловая система iOS учитывает регистр. Конфиденциальность регистра означает, что имена файлов и каталогов должны совпадать точно — README.txt и readme.txt будут считаться различными именами файлов.

Это может быть запутано для разработчиков .NET, которые более знакомы с файловой системой Windows, что является нечувствительным — файлы, ФАЙЛЫ и файлы будут ссылаться на один каталог.

Предупреждение

Симулятор iOS не учитывает регистр. Если регистр имени файла отличается от самого файла и ссылок на него в коде, код может по-прежнему работать в симуляторе, но он завершится ошибкой на реальном устройстве. Это одна из причин, почему важно развертывать и тестировать на фактическом устройстве рано и часто во время разработки iOS.

Разделитель пути

IOS использует косую черту вперед /" в качестве разделителя пути (который отличается от Windows, который использует обратную косую черту "\").

Из-за этой запутанной разницы рекомендуется использовать System.IO.Path.Combine метод, который корректирует текущую платформу, а не жесткий код определенного разделителя путей. Это простой шаг, который делает код более переносимым на другие платформы.

Песочница приложения

Доступ приложения к файловой системе (и другим ресурсам, таким как сетевые и аппаратные функции), ограничен по соображениям безопасности. Это ограничение называется песочницей приложения. С точки зрения файловой системы приложение ограничено созданием и удалением файлов и каталогов в домашнем каталоге.

Домашний каталог — это уникальное расположение в файловой системе, в которой хранится приложение и все его данные. Невозможно выбрать (или изменить) расположение домашнего каталога для приложения; однако iOS и Xamarin.iOS предоставляют свойства и методы для управления файлами и каталогами внутри.

Пакет приложений

Пакет приложений — это папка, содержащая приложение. Он отличается от других папок, добавив .app суффикс в имя каталога. Пакет приложений содержит исполняемый файл и все содержимое (файлы, изображения и т. д.), необходимые для проекта.

При переходе к пакету приложений в Mac OS он отображается с другим значком, чем в других каталогах (и .app суффикс скрыт). Однако это просто обычный каталог, который операционная система отображается по-другому.

Чтобы просмотреть пакет приложения для примера кода, щелкните проект правой кнопкой мыши в Visual Studio для Mac и выберите "Показать" в Finder. Затем перейдите к каталогу bin/ directory, где следует найти значок приложения (как показано на снимок экрана ниже).

Перейдите по каталогу bin, чтобы найти значок приложения, аналогичный этому снимку экрана

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

Содержимое пакета приложений

Пакет приложений — это то, что установлено на симуляторе или на устройстве во время тестирования, и в конечном счете это то, что отправлено Apple для включения в App Store.

Каталоги приложений

Когда приложение установлено на устройстве, операционная система создает домашний каталог для приложения и создает ряд каталогов в корневом каталоге приложения, доступных для использования. Так как iOS 8 каталоги, доступные для пользователей, не находятся в корневом каталоге приложения, поэтому пути для пакета приложений нельзя получить из каталогов пользователей или наоборот.

Эти каталоги, как определить путь и их цели перечислены ниже.

 

Directory Description
[ApplicationName].app/ В iOS 7 и более ранних версиях это каталог, в котором хранится ApplicationBundle исполняемый файл приложения. Структура каталогов, созданная в приложении, существует в этом каталоге (например, изображения и другие типы файлов, помеченные как ресурсы в проекте Visual Studio для Mac).

Если вам нужно получить доступ к файлам содержимого в пакете приложений, путь к этому каталогу доступен через NSBundle.MainBundle.BundlePath свойство.
Документы/ Используйте этот каталог для хранения пользовательских документов и файлов данных приложения.

Содержимое этого каталога можно сделать доступным для пользователя через общий доступ к файлам iTunes (хотя это отключено по умолчанию). Добавьте логический UIFileSharingEnabled ключ в файл Info.plist, чтобы разрешить пользователям доступ к этим файлам.

Даже если приложение не сразу включает общий доступ к файлам, следует избегать размещения файлов, которые должны быть скрыты от пользователей в этом каталоге (например, файлы базы данных, если вы не планируете предоставлять им общий доступ). Если конфиденциальные файлы остаются скрытыми, эти файлы не будут предоставляться (и потенциально перемещены, изменены или удалены iTunes), если общий доступ к файлам включен в будущей версии.

Этот метод можно использовать Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments) для получения пути к каталогу Documents для приложения.

Содержимое этого каталога резервное копирование выполняется iTunes.
Библиотека/ Каталог библиотеки — это хорошее место для хранения файлов, которые не создаются непосредственно пользователем, например базы данных или другие файлы, созданные приложением. Содержимое этого каталога никогда не предоставляется пользователю через iTunes.

Вы можете создать собственные подкаталоги в библиотеке; Однако здесь уже есть некоторые каталоги, созданные системой, которые следует учитывать, включая настройки и кэши.

Содержимое этого каталога (за исключением подкаталога кэшей) резервируется iTunes. Пользовательские каталоги, создаваемые в библиотеке, будут резервное копирование.
Библиотека и настройки/ Файлы предпочтений для конкретного приложения хранятся в этом каталоге. Не создавайте эти файлы напрямую. Вместо этого используйте NSUserDefaults класс.

Содержимое этого каталога резервное копирование выполняется iTunes.
Библиотека и кэши/ Каталог Caches — это хорошее место для хранения файлов данных, которые могут помочь в запуске приложения, но это может быть легко создано повторно. При необходимости приложение должно создавать и удалять эти файлы и повторно создавать эти файлы. IOS 5 также может удалить эти файлы (в ситуациях с низким хранилищем), однако это не делается во время работы приложения.

Содержимое этого каталога не резервируется iTunes, что означает, что они не будут присутствовать, если пользователь восстанавливает устройство, и они могут не присутствовать после установки обновленной версии приложения.

Например, если приложение не может подключиться к сети, вы можете использовать каталог Caches для хранения данных или файлов, чтобы обеспечить хороший автономный интерфейс. Приложение может быстро сохранять и извлекать эти данные во время ожидания сетевых ответов, но не требует резервного копирования и легко восстановить или повторно создать после восстановления или обновления версии.
tmp/ Приложения могут хранить временные файлы, необходимые только в течение короткого периода в этом каталоге. Чтобы сохранить пространство, файлы следует удалить, если они больше не требуются. Операционная система также может удалять файлы из этого каталога, если приложение не запущено.

Содержимое этого каталога не резервируется iTunes.

Например, каталог tmp может использоваться для хранения временных файлов, скачанных для отображения пользователю (например, аватаров Twitter или вложений электронной почты), но их можно удалить после просмотра (и скачать еще раз, если они требуются в будущем).

На этом снимке экрана показана структура каталога в окне Finder:

На этом снимке экрана показана структура каталога в окне Finder

Доступ к другим каталогам программным способом

Предыдущие примеры каталогов и файлов обращаются к каталогу Documents . Чтобы записать в другой каталог, необходимо создать путь с помощью синтаксиса ".". Как показано ниже:

var documents = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var library = Path.Combine (documents, "..", "Library");
var filename = Path.Combine (library, "WriteToLibrary.txt");
File.WriteAllText(filename, "Write this text into a file in Library");

Создание каталога аналогично:

var documents = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var library = Path.Combine (documents, "..", "Library");
var directoryname = Path.Combine (library, "NewLibraryDirectory");
Directory.CreateDirectory(directoryname);

Пути к каталогам Caches и tmp каталогам можно создать следующим образом:

var documents = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var cache = Path.Combine (documents, "..", "Library", "Caches");
var tmp = Path.Combine (documents, "..", "tmp");

Общий доступ к приложению "Файлы"

В iOS 11 появилось приложение "Файлы " — браузер файлов для iOS, позволяющий пользователю просматривать и взаимодействовать с файлами в iCloud, а также храниться в любом приложении, поддерживающем его. Чтобы разрешить пользователю напрямую получать доступ к файлам в приложении, создайте логический ключ в файле LSSupportsOpeningDocumentsInPlace Info.plist и задайте его следующим trueобразом:

Настройка LSSupportsOpeningDocumentsInPlace в Info.plist

Каталог документов приложения теперь будет доступен для просмотра в приложении "Файлы". В приложении "Файлы" перейдите в раздел "Мой iPhone", а каждое приложение с общими файлами будет отображаться. На снимках экрана ниже показано, как выглядит пример приложения:

Приложение для файлов iOS 11 Просмотр файлов iPhone Примеры файлов приложения

Совместное использование файлов с пользователем с помощью iTunes

Пользователи могут получить доступ к файлам в каталоге документов приложения, изменив Info.plist и создав приложение, которое поддерживает общий доступ iTunes (UIFileSharingEnabled) в представлении источника , как показано ниже:

Добавление приложения поддерживает свойство общего доступа iTunes

Эти файлы можно получить в iTunes, когда устройство подключено, и пользователь выбирает вкладку Apps . Например, на следующем снимке экрана показаны файлы в выбранном приложении, к которым предоставлен общий доступ через iTunes:

На этом снимке экрана показаны файлы в выбранном приложении, к которым предоставлен общий доступ через iTunes

Пользователи могут получить доступ только к элементам верхнего уровня в этом каталоге с помощью iTunes. Они не могут видеть содержимое любых подкаталогов (хотя они могут скопировать их на компьютер или удалить их). Например, с помощью GoodReader файлы PDF и EPUB можно предоставить приложению общий доступ, чтобы пользователи могли читать их на своих устройствах iOS.

Пользователи, изменяющие содержимое папки "Документы", могут вызвать проблемы, если они не осторожны. Ваше приложение должно учитывать это и быть устойчивым к разрушительным обновлениям папки "Документы".

Пример кода для этой статьи создает файл и папку в папке "Документы" (в SampleCode.cs) и включает общий доступ к файлам в файле Info.plist . На снимке экрана показано, как они отображаются в iTunes:

Снимок экрана: отображение файлов в iTunes

Сведения о настройке значков для приложения и любых пользовательских типов документов, создаваемых в статье "Работа с изображениями".

UIFileSharingEnabled Если ключ имеет значение false или отсутствует, общий доступ к файлам по умолчанию отключен, а пользователи не смогут взаимодействовать с каталогом Documents.

Резервное копирование и восстановление

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

  • [ApplicationName].app — не записывайте в этот каталог, так как он подписан и поэтому должен оставаться неизменным после установки. Он может содержать ресурсы, к которым вы обращаетесь из кода, но не требуют резервного копирования, так как они будут восстановлены путем повторной загрузки приложения.
  • Библиотека и кэши — каталог кэша предназначен для рабочих файлов, которые не требуют резервного копирования.
  • tmp — этот каталог используется для временных файлов, созданных и удаленных при отсутствии необходимости, или для файлов, которые iOS удаляет при необходимости пространства.

Резервное копирование большого объема данных может занять много времени. Если вы решили создать резервную копию любого документа или данных, приложение должно использовать папки "Документы и библиотека". Для временных данных или файлов, которые можно легко получить из сети, используйте кэши или каталог tmp.

Примечание.

IOS "очищает" файловую систему, когда устройство выполняется критически низко на диске. Этот процесс удаляет все файлы из папки библиотеки и кэша и tmp приложений, которые в настоящее время не выполняются.

Соблюдение ограничений резервного копирования iOS 5 iCloud

Примечание.

Хотя эта политика была впервые представлена с iOS 5 (что кажется давно давно), руководство по-прежнему относится к приложениям сегодня.

Apple представила функции резервного копирования iCloud с iOS 5. Если служба архивации iCloud включена, все файлы в домашнем каталоге приложения (за исключением каталогов, которые обычно не резервируются, например, пакет Cachesприложений и tmp) создают резервную копию на серверах iCloud. Эта функция предоставляет пользователю полную резервную копию в случае потери, кражи или повреждения устройства.

Так как iCloud предоставляет только 5 ГБ свободного места каждому пользователю и не требует необходимости использовать пропускную способность, Apple ожидает, что приложения будут резервное копирование только необходимых пользовательских данных. Чтобы соответствовать рекомендациям по хранилищу данных iOS, необходимо ограничить объем данных, которые будут резервированы, следуя приведенным ниже элементам:

  • Храните только созданные пользователем данные или данные, которые в противном случае не могут быть повторно созданы, в каталоге "Документы" (резервное копирование которого выполняется).
  • Храните любые другие данные, которые можно легко создать или повторно скачать или Library/Caches tmp (который не поддерживает резервное копирование и может быть "очищен").
  • Если у вас есть файлы, которые могут быть подходящими для Library/Caches папки или tmp папки, но вы не хотите быть "чистыми", сохраните их в другом месте (например Library/YourData) и примените атрибут "не резервное копирование", чтобы предотвратить использование пропускной способности резервного копирования iCloud и места хранения. Эти данные по-прежнему используют пространство на устройстве, поэтому следует тщательно управлять им и удалять их, когда это возможно.

Атрибут "не резервного копирования" задается с помощью NSFileManager класса. Убедитесь, что класс является using Foundation и вызывается SetSkipBackupAttribute следующим образом:

var documents = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var filename = Path.Combine (documents, "LocalOnly.txt");
File.WriteAllText(filename, "This file will never get backed-up. It would need to be re-created after a restore or re-install");
NSFileManager.SetSkipBackupAttribute (filename, true); // backup will be skipped for this file

true Если SetSkipBackupAttribute файл не будет создавать резервную копию, независимо от каталога, в который он хранится (даже Documents каталог). Атрибут можно запросить с помощью GetSkipBackupAttribute метода, и его можно сбросить, вызвав SetSkipBackupAttribute метод следующим falseобразом:

NSFileManager.SetSkipBackupAttribute (filename, false); // file will be backed-up

Совместное использование данных между приложениями iOS и расширениями приложений

Так как расширения приложений выполняются как часть ведущего приложения (в отличие от их содержащего приложения), общий доступ к данным не включается автоматически, поэтому требуется дополнительная работа. Группы приложений — это механизм использования iOS для предоставления другим приложениям общего доступа к данным. Если приложения были правильно настроены с правильными правами и подготовкой, они могут получить доступ к общему каталогу за пределами обычной песочницы iOS.

Настройка группы приложений

Общее расположение настраивается с помощью группы приложений, настроенной в разделе "Сертификаты, идентификаторы и профили" в Центр разработки iOS. Это значение также должно ссылаться в файле "Права каждого проекта.plist".

Сведения о создании и настройке группы приложений см. в руководстве по возможностям группы приложений .

Файлы

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

var FileManager = new NSFileManager ();
var appGroupContainer =FileManager.GetContainerUrl ("group.com.xamarin.WatchSettings");
var appGroupContainerPath = appGroupContainer.Path

Console.WriteLine ("Group Path: " + appGroupContainerPath);

// use the path to create and update files
...

Внимание

Если возвращен nullпуть к группе, проверьте конфигурацию прав и профиля подготовки и убедитесь, что они верны.

Обновления версий приложения

После скачивания новой версии приложения iOS создает новый домашний каталог и сохраняет в нем новый пакет приложений. Затем iOS перемещает следующие папки из предыдущей версии пакета приложений в новый домашний каталог:

  • Документы
  • Библиотека

Другие каталоги также могут быть скопированы и помещены в новый домашний каталог, но они не гарантированно копируются, поэтому ваше приложение не должно полагаться на это системное поведение.

Итоги

В этой статье показано, что операции файловой системы с Xamarin.iOS похожи на любое другое приложение .NET. Она также представила песочницу приложения и изучила последствия безопасности, которые она вызывает. Далее он изучил концепцию пакета приложений. Наконец, он перечислил специализированные каталоги, доступные вашему приложению, и объяснил их роли во время обновлений и резервных копий приложений.