处理陈旧的NSURL书签的正确方法是什么?

Geo*_*rge 23 cocoa nsurl security-scoped-bookmarks

从安全范围书签解析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"];
    }
  }];
}
Run Code Online (Sandbox Code Playgroud)

解析书签的代码

-(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);
  }
}
Run Code Online (Sandbox Code Playgroud)

当书签过时时,生成的已解析URL确实指向了正确的位置,尽管[url startAccessingSecurityScopedResource]返回YES,但我实际上无法访问该文件.

也许我误解了有关陈旧书签的文档,但我希望我只是做一些愚蠢的事情.每次重命名或移动书签文件/文件夹时弹出NSOpenPanel,此时我唯一的另一种选择,似乎很荒谬.

我应该补充一点,我有com.apple.security.files.bookmarks.app-scope,com.apple.security.files.user-selected.read-writecom.apple.security.app-sandbox都设置为true在我的权利文件中.

Geo*_*rge 19

经过大量令人失望的测试后,我得出以下结论.虽然逻辑,他们是令人失望的,因为用户所得到的经验是远远不够理想并根据开发商显著疼痛多远他们很愿意去帮助用户重新建立书签的资源引用.

当我在下面说"续订"时,我的意思是"使用从陈旧书签中解析的URL来生成一个新书签以替换旧书签".

  1. 只要在您的应用已具有访问权限的目录中移动或重命名已添加书签的资源,续订始终有效.因此,默认情况下,它始终在应用程序的容器文件夹中运行.

  2. 如果书签资源被移动到您的应用程序无权访问的文件夹中,则续订失败.例如,用户将容器文件夹中的文件夹拖到容器文件夹外的某个文件夹中.您将能够解析URL,但不能访问或更新书签.

  3. 如果书签资源存在于您的应用程序无权访问的文件夹中,则重新生成失败,然后重命名.这意味着用户可以明确授予您的应用程序对资源的访问权限,然后仅通过重命名就无意中撤消该访问权限.

  4. 如果资源移动到另一个卷,则解析失败.不确定这是一般书签的限制还是仅在沙盒应用程序中使用时.

对于问题2和3,由于书签URL的解析确实有效,因此您处于开发人员的合适位置.您至少可以通过告诉用户他们授予您的应用访问权限以及他们所在位置所需的资源来引导用户.通过让他们选择包含(直接或间接)更新书签所需的所有资源的文件夹,可以改善体验.这甚至可能是卷,如果他们愿意为您的应用程序提供这么多访问权限,它就完全解决了问题.

对于问题4,解决方案根本不起作用.由于无法解析新位置,用户必须在没有任何提示的情况下重新定位文件.我在当前的应用程序中做了一件事,减轻了这个问题的痛苦,就是为我存储书签的任何资源添加扩展属性.这样做至少让我让用户选择一个文件夹来搜索以前关联的资源.

令人沮丧的局限性,但书签仍然胜过存储静态路径.

  • @ user362515 只是为了确认一下,您是说在旧书签已解决但报告为陈旧且无法访问时创建新书签时,自 10.11 起您没有看到任何问题?具体来说,2号子弹不再是问题?如果是这样,那就太好了! (2认同)
  • 从 OS X 10.11 开始,从已解决的旧书签创建新的安全范围书签似乎没有问题。 (2认同)
  • 是的,只要在“bookmarkDataWithOptions”期间在新书签上运行“startAccessingSecurityScopedResource”。顺便说一句,大约 4) - 重命名/移动文件的书签解析仅适用于同一卷,这是记录在案的。 (2认同)