Как правильно обрабатывать устаревшие закладки NSURL?

При разрешении NSURL из закладки в области безопасности, если пользователь переименовал или переместил этот файл или папку, закладка устареет. В документе Apple говорится об этом в отношении устаревания:

isStale

По возвращении, если ДА, данные закладки устарели. Ваше приложение должно создать новую закладку, используя возвращенный URL, и использовать ее вместо любых сохраненных копий существующей закладки.

К сожалению, это редко работает для меня. Это может работать 5% времени. Попытка создать новую закладку с использованием возвращенного URL-адреса приводит к ошибке, код 256, и поиск в консоли выдает сообщение от sandboxd о том, что файл-чтение-данные запрещен в обновленном URL-адресе.

Примечание. Если регенерация закладки работает, она работает только при первом восстановлении. Кажется, что никогда не будет работать, если папка / файл будет перемещен / переименован снова.

Как я изначально создаю и сохраняю закладку

-(IBAction)bookmarkFolder:(id)sender {
  _openPanel = [NSOpenPanel openPanel];
  _openPanel.canChooseFiles = NO;
  _openPanel.canChooseDirectories = YES;
  _openPanel.canCreateDirectories = YES;
  [_openPanel beginSheetModalForWindow:self.window completionHandler:^(NSInteger result) {
    if (_openPanel.URL != nil) {
      NSError *error;
      NSData *bookmark = [_openPanel.URL bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
                                  includingResourceValuesForKeys:nil
                                                   relativeToURL:nil
                                                           error:&error];
      if (error != nil) {
        NSLog(@"Error bookmarking selected URL: %@", error);
        return;
      }
      NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
      [userDefaults setObject:bookmark forKey:@"bookmark"];
    }
  }];
}

Код, который разрешает закладку

-(void)resolveStoredBookmark {
  NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
  NSData *bookmark = [userDefaults objectForKey:@"bookmark"];
  if (bookmark == nil) {
    NSLog(@"No bookmark stored");
    return;
  }
  BOOL isStale;
  NSError *error;
  NSURL *url = [NSURL URLByResolvingBookmarkData:bookmark
                                         options:NSURLBookmarkResolutionWithSecurityScope
                                   relativeToURL:nil
                             bookmarkDataIsStale:&isStale
                                           error:&error];
  if (error != nil) {
    NSLog(@"Error resolving URL from bookmark: %@", error);
    return;
  } else if (isStale) {
    if ([url startAccessingSecurityScopedResource]) {
      NSLog(@"Attempting to renew bookmark for %@", url);
      // NOTE: This is the bit that fails, a 256 error is 
      //       returned due to a deny file-read-data from sandboxd
      bookmark = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
               includingResourceValuesForKeys:nil
                                relativeToURL:nil
                                        error:&error];
      [url stopAccessingSecurityScopedResource];
      if (error != nil) {
        NSLog(@"Failed to renew bookmark: %@", error);
        return;
      }
      [userDefaults setObject:bookmark forKey:@"bookmark"];
      NSLog(@"Bookmark renewed, yay.");
    } else {
      NSLog(@"Could not start using the bookmarked url");
    }
  } else {
    NSLog(@"Bookmarked url resolved successfully!");
    [url startAccessingSecurityScopedResource];
    NSArray *contents = [NSFileManager.new contentsOfDirectoryAtPath:url.path error:&error];
    [url stopAccessingSecurityScopedResource];
    if (error != nil) {
      NSLog(@"Error reading contents of bookmarked folder: %@", error);
      return;
    }
    NSLog(@"Contents of bookmarked folder: %@", contents);
  }
}

Когда закладка устарела, результирующий разрешенный URL-адрес действительно указывает на правильное местоположение, я просто не могу получить доступ к файлу, несмотря на тот факт, что [url startAccessingSecurityScopedResource] возвращает YES.

Возможно, я неправильно истолковываю документацию, касающуюся устаревших закладок, но я надеюсь, что просто делаю глупости. Высовывать NSOpenPanel каждый раз, когда файл / папка с закладкой переименовывается или перемещается, мой единственный другой вариант на данный момент кажется нелепым.

Я должен добавить, что у меня com.apple.security.files.bookmarks.app-scope, com.apple.security.files.user-selected.read-write и com.apple.security.app-sandbox, все установлено в true в моем файле прав.

1 ответ

Решение

После многих разочаровывающих испытаний я пришел к следующим выводам. Хотя это и логично, но они разочаровывают, поскольку получаемый опыт для пользователей далек от идеального, а разработчики испытывают большую боль в зависимости от того, насколько далеко они готовы пойти, чтобы помочь пользователям восстановить ссылки на ресурсы, добавленные в закладки.

Когда я говорю "обновить" ниже, я имею в виду "создать новую закладку, чтобы заменить устаревшую закладку, используя URL, разрешенный из устаревшей закладки".

  1. Обновление всегда работает до тех пор, пока ресурс, отмеченный закладкой, перемещен или переименован в каталоге, доступ к которому у вашего приложения уже есть. Таким образом, по умолчанию он всегда работает внутри папки контейнера вашего приложения.

  2. Обновление не выполняется, если ресурс, добавленный в закладки, перемещен в папку, к которой у вашего приложения нет прав доступа. Например, пользователь перетаскивает папку из папки контейнера в какую-либо папку вне папки контейнера. Вы сможете разрешить URL-адрес, но не сможете открыть или обновить закладку.

  3. Обновление не выполняется, если ресурс, добавленный в закладки, находится в папке, к которой ваше приложение не имеет доступа, и затем переименовывается. Это означает, что пользователь может явно предоставить вашему приложению доступ к ресурсу, а затем непреднамеренно отозвать этот доступ, просто переименовав его.

  4. Не удается выполнить разрешение, если ресурс перемещен на другой том. Не уверен, является ли это ограничением для закладок в целом или только при использовании в изолированном приложении.

Для вопросов 2 и 3 вы занимаетесь достойным положением в качестве разработчика, поскольку разрешение URL-адреса с закладкой работает. Вы можете, по крайней мере, привести пользователя, указав ему, какие именно ресурсы им нужны для предоставления доступа к вашему приложению и где они находятся. Опыт может быть улучшен, если они выберут папку, которая содержит (прямо или косвенно) все ресурсы, для которых вам нужно обновить закладку. Это может быть даже том, который решает проблему полностью, если они хотят предоставить вашему приложению такой большой доступ.

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

Разочаровывающие ограничения, но закладки по-прежнему выигрывают при хранении статических путей.

Другие вопросы по тегам