Тестовая среда приложения: переименование файла не работает
У меня есть базовое приложение Cocoa, которое позволяет пользователю управлять списком файлов. Файлы добавляются с помощью перетаскивания, и я сохраняю права доступа в защищенной закладке при перезапуске приложения.
Все идет нормально. Приложению разрешено чтение и запись в файлы пользователя, но переименование завершается с ошибкой разрешения, утверждающей, что моему приложению не разрешен доступ к родительской папке.
Код:
[[NSFileManager defaultManager] moveItemAtPath:currentPath
toPath:newPath error:&error]
Ошибка:
Error Domain=NSCocoaErrorDomain Code=513 "“Some image.jpg” couldn’t
be moved because you don’t have permission to access “some folder”
Клянусь, это работало только вчера, ничего не изменилось... Во всяком случае. Я бы предположил, что если пользователь разрешает доступ к файлу через диалоговое окно "Открыть" или перетаскивает его, то изолированному приложению должно быть разрешено переименовать файл.
2 ответа
Проблема, с которой вы сталкиваетесь, заключается не в сохранении чего-либо, а в том, как работает песочница:
Правило...
Чтобы переименовать файл (фактически выполнить операцию перемещения),
у вас должен быть доступ для записи в родительский каталог указанного файла.
Наша проблема...
Если вы перетаскиваете файлы, песочница будет только расширять доступ к этим файлам, а не к их родительскому каталогу, следовательно, эта ошибка говорит о том, что ей нужно разрешение
Теперь попробуйте перетащить папку, содержащую эти файлы, и вы увидите, что все просто работает:)
Так что же нам делать?
Простым решением было бы попросить пользователя выбрать через NSOpenPanel "рабочий" каталог, чтобы у песочницы был доступ без отчуждения пользователя каждый раз, когда он хочет переименовать
Но теперь мы беспокоим пользователя за дерьмо, о котором он даже не должен знать!
Так что для меня это плохой дизайн / UX
Теперь я прочитал документы о песочнице и заметил, что в NSFileCoordinator есть метод itemAtURL: willMoveToURL:
Который привел меня к этому небольшому фрагменту (который я переписал здесь, потому что в нем отсутствовала функция willMove)
что мы хотим сделать, это попросить расширение для песочницы ради переименования:
NSURL *sourceURL = document.fileURL;
NSURL *destinationURL = [[sourceURL URLByDeletingLastPathComponent] URLByAppendingPathComponent:fileName isDirectory:NO];
NSError *writeError = nil;
__block NSError *moveError = nil;
__block BOOL success = NO;
NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
[coordinator coordinateWritingItemAtURL:sourceURL
options:NSFileCoordinatorWritingForMoving
writingItemAtURL:destinationURL
options:NSFileCoordinatorWritingForReplacing
error:&writeError
byAccessor:^(NSURL *newURL1, NSURL *newURL2)
{
NSFileManager *fileManager = [NSFileManager new];
[coordinator itemAtURL:sourceURL willMoveToURL:destinationURL];
success = [fileManager moveItemAtURL:newURL1 toURL:newURL2 error:&moveError];
if (success)
{
[coordinator itemAtURL:newURL1 didMoveToURL:newURL2];
}
}];
К сожалению, кажется, что этот метод предназначен только для изменения расширения файла, а не переименования, следовательно, эта ошибка в журнале:
NSFileSandboxingRequestRelatedItemExtension: an error was received from pboxd instead of a token. Domain: NSPOSIXErrorDomain, code: 1
Yay, яблоко, Yay
Следующее работает для меня, пока я добавляю myext
как связанный тип документа в моем Info.plist
, Это отмечено в документации Apple.
Вот выдержка:
В обоих сценариях вы должны внести небольшое изменение в файл Info.plist приложения. Ваше приложение должно уже объявить массив Document Types (CFBundleDocumentTypes), который объявляет типы файлов, которые может открыть ваше приложение.
Для каждого словаря типов файлов в этом массиве, если этот тип файлов следует рассматривать как потенциально связанный тип для целей открытия и сохранения, добавьте ключ NSIsRelatedItemType с логическим значением YES.
Код изменен из ответа BenRhayader:
NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
NSURL *sourceURL = chosenFile;
NSURL *destinationURL = [chosenFile URLByAppendingPathExtension: @"myext"];
[coordinator coordinateWritingItemAtURL:sourceURL
options:NSFileCoordinatorWritingForMoving
writingItemAtURL:destinationURL
options:NSFileCoordinatorWritingForReplacing
error:NULL
byAccessor:^(NSURL *newURL1, NSURL *newURL2)
{
NSFileManager *fileManager = [NSFileManager new];
[coordinator itemAtURL:sourceURL willMoveToURL:destinationURL];
NSError *moveError;
BOOL success = [fileManager moveItemAtURL:newURL1 toURL:newURL2 error:&moveError];
if (success)
{
[coordinator itemAtURL:newURL1 didMoveToURL:newURL2];
}
}];