Функция чтения файлов macOS, разработанная на удалённом Mac, работает в тестовом каталоге, но у пользователей выдаёт ошибку «нет доступа». Часто дело в том, что тестовый файл находился внутри контейнера самого приложения или проверялось только чтение сразу после выбора файла, но не после выхода из приложения. Чтобы проверить такую функцию, возьмите файл вне контейнера, который пользователь выберет сам, и убедитесь, что приложение сможет прочитать его и после перезапуска.
Сначала определите границы проверки
App Sandbox ограничивает доступ приложения к файлам за пределами его контейнера. Если пользователь предоставил доступ через системное окно выбора файла, это не означает, что приложение сможет в дальнейшем читать файл по исходному пути. Если приложению нужно запомнить файл, оно должно сохранить закладку с ограниченной областью доступа, разрешить её при следующем запуске и активировать доступ на время чтения или записи.
Подготовьте обычный текстовый файл с легко узнаваемым содержимым и разместите его вне контейнера приложения. Не выбирайте каталог, дополнительно защищённый системными разрешениями на конфиденциальность, чтобы не смешивать при проверке два механизма авторизации. Для сравнения подготовьте файл внутри контейнера: если он читается, это подтверждает лишь работу базового кода обработки файлов, но не доступ к внешним файлам.
Критерий приёмки — не «после закрытия окна выбора файла ошибка не появилась», а «после перезапуска приложение по-прежнему читает указанный файл в пределах предоставленного доступа и предлагает способ восстановить доступ, если разрешение недействительно».
Проверьте права доступа в поставляемом приложении
Проверяйте фактически поставляемый .app, а не только файл прав доступа, который редактировали в проекте. Из-за настроек сборки или процесса подписания права в конечном пакете могут отличаться. Замените APP ниже на фактический путь к результату текущей сборки:
APP="$PWD/build/Reader.app"
codesign --verify --strict --verbose=2 "$APP"
codesign -d --entitlements :- "$APP"
Убедитесь, что песочница включена. Если файл импортируется только для чтения, проверьте право на чтение файлов, выбранных пользователем; право на чтение и запись рассматривайте лишь тогда, когда действительно нужно изменять исходный файл. Если доступ требуется сохранять между запусками, проверьте также объявленное право на закладки уровня приложения. Не расширяйте доступ до посторонних каталогов только ради успешного прохождения теста.
Проверка подписи показывает лишь то, какие права объявлены. Она не подтверждает, что пользователь выбрал файл или что закладку удаётся восстановить. И то и другое нужно проверять в работающем приложении.
Сохраняйте закладку и завершайте каждое обращение к файлу
После того как NSOpenPanel вернёт URL выбранного пользователем файла, создайте закладку с ограниченной областью доступа и сохраните полученные Data в постоянном хранилище приложения. Не сохраняйте только url.path: путь указывает расположение файла, но не содержит сведений, необходимых для повторного получения доступа.
Разрешение закладки, начало и завершение доступа выполняйте в одной функции чтения, чтобы не пропустить освобождение ресурса при ошибке:
enum FileAccessError: Error {
case staleBookmark
case accessDenied
}
func readSelectedFile(bookmark: Data) throws -> Data {
var stale = false
let url = try URL(
resolvingBookmarkData: bookmark,
options: .withSecurityScope,
relativeTo: nil,
bookmarkDataIsStale: &stale
)
guard !stale else { throw FileAccessError.staleBookmark }
guard url.startAccessingSecurityScopedResource() else {
throw FileAccessError.accessDenied
}
defer { url.stopAccessingSecurityScopedResource() }
return try Data(contentsOf: url)
}
При создании закладки тоже используйте опцию ограниченной области доступа и надёжно сохраняйте возвращённые данные. В примере чтение прекращается, если закладка устарела; в интерфейсе приложения должна быть возможность заново выбрать файл и после получения нового разрешения создать новую закладку. Результат startAccessingSecurityScopedResource() обязательно нужно проверять, а вызов stopAccessingSecurityScopedResource() нельзя откладывать до выхода из приложения.
Проверьте работу после перезапуска в графическом сеансе
На облачном Mac подпись можно проверить через командную строку, но для проверки взаимодействия с NSOpenPanel нужен работающий графический сеанс macOS. Возможность выполнить задачу по SSH не означает, что окно выбора файла сможет открыться. При автоматизированном тестировании также убедитесь, что тестовый процесс работает в окружении с графическим сеансом.
Выполните следующие шаги с одним и тем же тестовым файлом, записывая для каждого ожидаемый и фактический результат:
- При первом запуске, не открывая окно выбора файла, попробуйте прочитать внешний файл напрямую по ранее сохранённому пути. Убедитесь, что один лишь путь не позволяет приложению обойти предусмотренное разрешение.
- Выберите файл в окне, прочитайте и проверьте его содержимое, затем сохраните закладку.
- Полностью завершите приложение и снова запустите тот же поставляемый пакет. Разрешите закладку, повторно прочитайте файл и проверьте его содержимое.
- Переместите тестовый файл и повторите попытку. Приложение должно показать понятную ошибку и предложить выбрать файл заново, а не сообщать об ошибке подписи вместо отсутствующего файла.
- Проверьте обработку ошибки с повреждёнными данными закладки. Приложение должно прекратить чтение и запросить повторный выбор файла, не открывая окна снова и снова и не повторяя попытки бесконечно.
Третий шаг пропускают чаще всего: чтение в пределах одного процесса может скрыть тот факт, что закладка не была сохранена в постоянном хранилище. Если тест предусматривает запись изменений в файл, отдельно повторите всю последовательность с правом на чтение и запись: проверка только чтения её не заменяет.
Ищите причину сбоя по собранным данным
Если чтение не удаётся сразу после выбора файла, сначала проверьте, правильно ли используется URL, возвращённый окном выбора, и соответствуют ли объявленные права требуемым операциям чтения или записи. Если сразу всё работает, а после перезапуска — нет, в первую очередь проверьте, действительно ли закладка сохранена и сохранились ли у повторно запущенного приложения прежние идентификатор и настройки подписи. Если сбой возникает только в отдельных каталогах, сначала исключите влияние их дополнительных разрешений на конфиденциальность, а не ослабляйте ограничения песочницы.
В записи о регрессионной проверке сохраните как минимум версию поставляемого приложения, окончательный набор объявленных прав, категорию каталога с тестовым файлом, результаты до и после выбора файла, результат после перезапуска и тип ошибки. Записывайте в журнал только необходимые состояния и ошибки; не выводите исходные данные закладки или содержимое файла. Тогда при сбое в следующей сборке команда сможет определить, изменились ли объявленные права, нарушен ли жизненный цикл закладки или в тестовом окружении нет графического сеанса, — вместо того чтобы снова строить догадки на основании «у меня на компьютере открывается».
Часто задаваемые вопросы
Зачем сохранять закладку после выбора файла?
Если файл понадобится после перезапуска, сохраните закладку с областью безопасности. Затем разрешите её, откройте доступ на время чтения и закройте его после операции.
Можно ли выполнить всю проверку только через SSH?
Нет. Через командную строку можно проверить подпись и разрешения, но выбор файла и интерактивная проверка требуют доступного графического сеанса macOS.
Подключите облачный Mac к своему рабочему процессу
VPSPush предоставляет выделенные физические Mac с удалённым доступом. Выберите локацию и срок аренды, затем проверьте данные для подключения и итоговую сумму в заказе.