Когда команда отлаживает iOS-приложение на облачном Mac, легко принять успешный предварительный просмотр PDF, выбранного через приложение «Файлы», за завершённый импорт. Но приложение могло лишь временно получить доступ к внешнему файлу. При следующем запуске прежний путь может стать недоступным, и запись в списке чтения не откроется. Проверять нужно не то, появляется ли окно выбора, а права доступа, принадлежность файла и срок его доступности после передачи приложению внешним поставщиком.
Разберём это на примере импорта PDF для длительного хранения и чтения. К машине разработки можно подключиться удалённо, но правила доступа к файлам определяет iOS-устройство, на котором работает приложение. Разделим реализацию импорта и автоматизированные тесты: первая обрабатывает URL, полученный от настоящего окна выбора, вторые проверяют собственную копию приложения и сценарии ошибок.
Определите, когда импорт считается завершённым
Открыть файл — не значит получить собственную копию. При использовании UIDocumentPickerViewController(forOpeningContentTypes: [.pdf], asCopy: false) URL в обратном вызове указывает на расположение, предоставленное окном выбора. Его нельзя просто сохранить в записи списка чтения как постоянный путь. Если документ должен открываться и при следующем запуске, оригинал нужно прочитать, пока доступ к нему действителен, скопировать в постоянный каталог приложения и связать запись с новой копией.
Заранее задайте три наблюдаемых результата: отмена выбора не создаёт запись; ошибка копирования не создаёт запись об успешном импорте; после успешного импорта приложение открывает свою копию даже после перемещения оригинала и перезапуска. Временный каталог подходит для промежуточного этапа импорта, но не для постоянной библиотеки. Если запись должна храниться долго, переместите проверенную временную копию в постоянный каталог приложения, например Application Support.
Считать импорт успешным следует после записи и проверки копии, а не в момент, когда окно выбора вернуло URL.
Скопируйте файл с координацией, пока доступ действителен
Получив URL в обратном вызове окна выбора, сначала откройте доступ через область безопасности, затем согласуйте чтение с помощью NSFileCoordinator. Функция ниже показывает только временное сохранение одного небольшого PDF. Вызывающему коду ещё предстоит решить, как проверить содержимое, перенести файл в постоянный каталог и обновить запись. Пример использует режим открытия asCopy: false; его не следует превращать в универсальную функцию копирования, которая считает любой источник внешним файлом с предоставленным доступом.
func stagePickedPDF(_ url: URL) throws -> URL {
guard url.startAccessingSecurityScopedResource() else {
throw CocoaError(.fileReadNoPermission)
}
defer { url.stopAccessingSecurityScopedResource() }
let destination = FileManager.default.temporaryDirectory
.appendingPathComponent(UUID().uuidString)
.appendingPathExtension("pdf")
var coordinationError: NSError?
var copyError: Error?
NSFileCoordinator().coordinate(
readingItemAt: url,
options: [],
error: &coordinationError
) { readableURL in
do {
try FileManager.default.copyItem(
at: readableURL,
to: destination
)
} catch {
copyError = error
}
}
if let coordinationError { throw coordinationError }
if let copyError { throw copyError }
return destination
}
Вызов coordinate синхронный: копирование большого файла не должно надолго блокировать поток интерфейса. В рабочей реализации весь процесс — открытие доступа, согласованное чтение и закрытие доступа — можно выполнять в фоновой задаче. Не закрывайте доступ в обратном вызове перед тем, как передать внешний URL фоновой задаче. При ошибке доступ тоже необходимо закрыть; здесь defer проще проверить, чем несколько вызовов закрытия в разных ветках.
Превратите временную копию в восстанавливаемую запись
После копирования убедитесь, что файл существует и его размер выглядит правдоподобно, а затем попробуйте открыть его PDF-парсером: одно расширение .pdf не подтверждает корректность содержимого. Если проверка прошла успешно, переместите файл в постоянный каталог приложения и сохраните относительный путь под идентификатором, созданным приложением. Если проверка или перемещение не удались, удалите временный файл, покажите пользователю ошибку с возможностью повторить попытку и не добавляйте документ в список чтения.
При сохранении данных учитывайте и промежуточный сбой: файл уже записан, а запись о нём — ещё нет. Один из вариантов — сначала создать запись в состоянии ожидания, а после перемещения пометить её как доступную. При запуске приложение может проверять незавершённые записи, сверять их с файлами и удалять остатки неудачных операций. Конкретная схема зависит от способа хранения данных, но цель одна: в списке не должно быть элементов, указывающих на несуществующий файл.
Не добавляйте отображаемое имя, предоставленное окном выбора, непосредственно в целевой путь. Создавайте имя файла в приложении, а исходное сохраняйте как поле для отображения. Это предотвращает перезапись при совпадении имён и снижает риск ошибок из-за символов в пути. Если продукт должен всегда читать внешний оригинал, а не хранить копию, потребуется другая модель доступа: отдельно продумайте повторное предоставление прав и интерфейс на случай недоступности источника. Использовать для этого состояние «импортировано локально» нельзя.
Проверяйте импорт по уровням на облачном Mac
Сначала с помощью XCTest подайте логике после импорта образцы PDF из песочницы приложения. Проверьте успешный разбор, ошибку для повреждённого файла, отсутствие перезаписи при совпадении имён и то, что после удаления временного файла запись в списке по-прежнему указывает на постоянную копию. Не выдавайте этот уровень тестов за проверку доступа через область безопасности: у файлов из песочницы обычно нет условий доступа, характерных для настоящего поставщика файлов.
Затем пройдите весь интерфейс в симуляторе. Подготовьте PDF, видимый в окне выбора приложения «Файлы», и проверьте отдельно выбор файла и отмену. После импорта закройте и снова запустите приложение, убедившись, что запись в списке чтения открывается. Затем попробуйте убрать исходный файл из доступного через окно выбора расположения и проверьте, действительно ли приложение читает собственную копию. Поведение поставщиков, которым нельзя надёжно управлять автоматически, заносите в список ручных проверок; не маскируйте неопределённость ожиданием фиксированного числа секунд.
Наконец, проверьте права доступа и загрузку на целевом устройстве с теми поставщиками файлов, которыми будут пользоваться на практике. Некоторые документы в момент выбора ещё не полностью загружены на устройство. Если чтение не удалось, интерфейс должен показать ошибку и позволить выбрать файл снова, а не создавать пустую копию. Здесь проверяется обработка ошибки, а не гарантируется время загрузки со стороны поставщика.
Завершите проверку матрицей ошибок
| Действие | Ожидаемый результат |
|---|---|
| Отмена выбора | Список чтения не изменился, временных файлов не осталось |
| Источник недоступен для чтения | Показана понятная ошибка, запись об успешном импорте не создана |
| Проверка копии не пройдена | Временный файл удалён, доступна повторная попытка |
| Источник перемещён после импорта | После перезапуска копия приложения по-прежнему открывается |
| Приложение прервано во время импорта | При следующем запуске незавершённая запись обнаружена и обработана |
К результатам проверки полезно приложить тип файла, категорию источника, версии системы и сборки приложения, а также фактический результат каждого действия. Тогда при воспроизведении проблемы через удалённый рабочий стол команда сможет сначала определить, где произошёл сбой — при предоставлении доступа, согласованном чтении, копировании, проверке файла или сохранении записи, — вместо того чтобы снова и снова выбирать один и тот же файл.
Часто задаваемые вопросы
Можно ли сохранить путь, полученный из окна выбора документа?
Не считайте внешний путь постоянной ссылкой. Для долгого использования согласуйте чтение при действующем доступе и скопируйте файл в постоянное хранилище приложения.
Достаточно ли симулятора для проверки импорта документов?
Нет. В нём удобно проверять копирование, обработку и ошибки, но разрешения, отмену выбора и перемещение исходного файла нужно проверить и с реальным поставщиком файлов.
Нужен выделенный физический Mac с удалённым доступом?
Сравните конфигурации M4 Start, M4 Work и M4 Pro, расположение узлов и периоды оплаты, чтобы выбрать подходящий вариант.