Хороший способ управлять папкой "Документы/Входящие" в приложении iOS

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

Мне неудобно это простое решение (A), потому что моему приложению нужно взаимодействовать с пользователем, прежде чем он сможет закончить обработку и удаление файла. Если пользователь приостанавливает приложение в течение этого периода взаимодействия, и приложение затем убивается, когда оно находится в фоновом режиме, устаревший файл не будет удален, когда приложение запустится в следующий раз. Конечно, я могу улучшить свое приложение, чтобы охватить этот сценарий, но я подозреваю, что всегда будет другой случай с границей, который оставит меня с "нечистой" папкой Documents/Inbox.

Таким образом, предпочтительным решением (B) было бы удаление папки Documents/Inbox в соответствующее время (например, когда приложение запускается нормально, то есть не через взаимодействие с документом). Мне все еще неудобно, потому что я получаю доступ к пути файловой системы, местоположение которого официально не документировано нигде. Например, мое приложение сломается, если в будущей версии iOS система взаимодействия с документом больше не помещает файлы в Document/Inbox.

Итак, мои вопросы:

  • Вы порекомендовали бы решение A или B?
  • Используете ли вы другой подход и можете ли вы дать описание того, как ваше приложение управляет Document/Inbox?
  • И последнее, но не менее важное: знаете ли вы часть официальной документации Apple, которая охватывает тему и что я пропустил?

Ответ 1

Поскольку я задал этот вопрос, я применил следующее решение:

  • Я изменил свое приложение, так что теперь он немедленно обрабатывает файл, переданный ему через взаимодействие с документацией, без привлечения пользователя вообще. Если приложение не выйдет из строя или не будет приостановлено и убито, в середине обработки файла, это должно всегда оставлять меня с чистым Documents/Inbox.
  • Чтобы закрыть (редкий) случай сбоя или приостановить/убить, мое приложение удаляет папку Documents/Inbox, когда она запускается нормально (т.е. без цели взаимодействия с документом). Для этого путь к папке Documents/Inbox обязательно должен быть жестко запрограммирован.

Вот мысли, которые вошли в решение:

  • Редизайн приложения
    • Первоначально было бы неплохо предложить пользователю выбор, как она хочет, чтобы файл обрабатывался. В конце концов, предлагая выбор, приложение станет более гибким и предоставит пользователю больше свободы, не так ли?
    • Тогда я понял, что я пытаюсь переложить ответственность за принятие решения о том, как взаимодействие с документом должно быть обработано пользователю. Таким образом, я укусил пулю, принял жесткие решения, а затем успешно пошел на внедрение простой и понятной системы взаимодействия документов в моем приложении.
    • Как оказалось, никакое взаимодействие с пользователем также не означает, что приложение проще в использовании, поэтому здесь выигрышная ситуация, как для меня как для разработчика, так и для пользователей моего приложения.
  • Удаление папки Documents/Inbox во время запуска приложения
    • Удаление папки Documents/Inbox во время запуска приложения является лишь запоздалой мыслью, а не важной частью того, как мое приложение обрабатывает взаимодействие с документом
    • Поэтому я вполне согласен рискнуть, что Apple может в какой-то момент в будущем изменить путь к файловой системе папки "Входящие". Самое худшее, что может случиться, это то, что мое приложение начнет накапливать несколько файлов, оставшихся от сценариев сбоя или приостановки/уничтожения.

И, наконец, несколько соображений для дальнейшего развития:

  • Если выяснится, что на самом деле должно быть несколько способов, как приложение может обрабатывать взаимодействие с документами, я бы добавил предпочтение пользователя, чтобы пользователь принимал решение заранее, а приложение не нуждалось чтобы остановить его обработку, чтобы попросить пользователя выбрать.
  • Если окажется, что взаимодействие пользователя абсолютно неизбежно в середине процесса обработки взаимодействия с документом, я бы посмотрел на этот подход: 1) Прежде чем пользователю разрешат взаимодействовать, переместите файл с Documents/Inbox в некоторый своего рода "промежуточная" папка; 2) Пусть взаимодействие пользователя происходит; 3) Обработайте файл в папке "staging", любым способом, который пользователь выбрал. Важно то, что папка "промежуточная" находится в известном месте и полностью управляется приложением. Если пользователь приостанавливает и затем убивает приложение в середине этапа взаимодействия с пользователем, соответствующее действие можно просто предпринять, когда приложение будет запущено в следующий раз.

ИЗМЕНИТЬ

В iOS 7 невозможно удалить Documents/Inbox после его создания. Метод NSFileManager removeItemAtPath:error: возвращает Cocoa ошибку 513, которая разрешает NSFileWriteNoPermissionError (cf. этот список базовых констант). Ошибка, похоже, не связана с разрешениями POSIX, однако она скорее выглядит так, как будто сама система мешает попытке удаления (возможно, защита структуры пакета приложения?).

Также следует отметить, что в настоящее время Apple явно объявляет Documents/Inbox в документации метода UIApplicationDelegate application:openURL:sourceApplication:annotation:. Они также говорят, что

[...] У вашего приложения есть разрешение на чтение и удаление файлов в этом каталоге, но у него нет разрешения на их запись. Если вы хотите изменить файл, вы должны перенести его в другой каталог.

Существует больше сведений о возможном шифровании файлов в документах, но вы должны это прочитать сами.

Ответ 2

Эта проблема значительно усложнилась с появлением приложения "Файлы" и функции "Открыть на месте".

Если у вас нет включения "Поддержка открытия документов на месте" в info.plist, то все почти так же, и файлы, открытые из любого другого приложения, по-прежнему отображаются в каталоге Documents/Inbox., Но файлы, открытые из приложения "Файлы", появляются в другом почтовом ящике, в данный момент находящемся в tmp/<bundle ID of app>-inbox. По-прежнему рекомендуется удалять файл, как только вы закончите с ним, но нет необходимости периодически чистить каталог, потому что каталог tmp очищается iOS время от времени в любом случае.

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

Само собой разумеется, что если вы получите такой файл, вы не должны удалять его. Но если файл поступает в папку "Входящие", что также часто случается, вы должны удалить его. Чтобы определить это, параметры вызова application:openURL:options: содержат клавишу UIApplicationOpenURLOptionsOpenInPlaceKey. Если это имеет значение (NSNumber) НЕТ, то файл находится в папке "Входящие", и его следует удалить. Если оно имеет значение YES, оно открывается на месте и не должно быть удалено.

Обратите внимание, что для файлов, которые открываются на месте, вам также необходимо сначала получить разрешение на их использование. Вы делаете это, но окружаете доступ к файлу вызовами startAccessingSecurityScopedResource и stopAccessingSecurityScopedResource. Подробности смотрите в документации Apple.

Ответ 3

Сейчас я столкнулся с этой же проблемой. Как и у вас, у меня нет хорошего решения, но в ответ на ваши вопросы я склоняюсь к варианту A по сравнению с вариантом B, потому что мне не нравится идея потенциально иметь проблему с будущими версиями ОС. Поскольку в моем случае нет взаимодействия с пользователем, когда я показываю предварительный просмотр документа, я могу продолжить его и удалить, когда получаю обратный вызов делегата –documentInteractionControllerDidEndPreview:. Это теоретично в том, что я еще не закодировал это и не получаю к нему какое-то время, поскольку это элемент с низким приоритетом. Если он не работает или есть другие проблемы, я отчитаюсь здесь. Поиск Google, который я ввел, чтобы найти документацию от Apple, указал на этот пост StackOverflow. Я не видел никакой другой полезной информации от Apple или кого-либо еще по этому вопросу.