Тестовая среда приложения: переименование файла не работает

У меня есть базовое приложение 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];
        }
    }];
Другие вопросы по тегам