Подтвердить что ты не робот

Каков правильный способ обработки устаревших закладок 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 в моем файле прав.

4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

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