29

セキュリティ スコープのブックマークから NSURL を解決するときに、ユーザーがそのファイルまたはフォルダーの名前を変更または移動した場合、ブックマークは古くなります。Apple のドキュメントには、古さについて次のように書かれています。

古い

返された場合、YES の場合、ブックマーク データは古くなっています。アプリは、返された URL を使用して新しいブックマークを作成し、保存されている既存のブックマークのコピーの代わりに使用する必要があります。

残念ながら、これがうまくいくことはめったにありません。5% の確率で機能する場合があります。返された URL を使用して新しいブックマークを作成しようとすると、コード 256 のエラーが発生し、コンソールを見ると、更新された 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-scopecom.apple.security.files.user-selected.read-write、およびcom.apple.security.app-sandboxをすべて true に設定していることを追加する必要があります私の資格ファイルに。

4

1 に答える 1

25

多くの残念なテストの後、私は次の結論に達しました。論理的ではありますが、結果として得られるユーザー エクスペリエンスは理想とはほど遠いものであり、ユーザーがブックマークされたリソースへの参照を再確立するのを支援するためにどこまで進んでいるかに応じて、開発者にとっては大きな苦痛になるため、残念です。

以下の「更新」とは、「古いブックマークから解決された URL を使用して、古いブックマークを置き換える新しいブックマークを生成する」という意味です。

  1. アプリが既にアクセス許可を持っているディレクトリ内でブックマークされたリソースが移動または名前変更されている限り、更新は常に機能します。したがって、デフォルトでは、常にアプリケーションのコンテナー フォルダー内で機能します。

  2. ブックマークされたリソースが、アプリケーションがアクセスする権限を持たないフォルダーに移動された場合、更新は失敗します。例: ユーザーがコンテナ フォルダからコンテナ フォルダ外のフォルダにフォルダをドラッグします。URL を解決することはできますが、ブックマークにアクセスしたり更新したりすることはできません。

  3. ブックマークされたリソースが、アプリケーションがアクセスできないフォルダーにあり、名前が変更された場合、更新は失敗します。これは、ユーザーがアプリケーションにリソースへのアクセスを明示的に許可した後、リソースの名前を変更するだけでそのアクセスをうっかり取り消すことができることを意味します。

  4. リソースが別のボリュームに移動されると、解決に失敗します。これが一般的なブックマークの制限なのか、それともサンドボックス アプリケーションで使用されたときだけなのかは不明です。

問題 2 と 3 については、ブックマークされた URL の解決が機能するため、開発者として適切な立場にいます。少なくとも、アプリへのアクセスを許可する必要があるリソースとその場所を正確に伝えることで、ユーザーを導くことができます。ブックマークを更新する必要があるすべてのリソースを (直接的または間接的に) 含むフォルダーを選択することで、エクスペリエンスが向上する可能性があります。これは、アプリケーションにこれだけ多くのアクセスを許可する場合、問題を完全に解決するボリュームである可能性もあります。

問題 4 については、解決策がまったく機能しません。新しい場所を解決できないため、ユーザーはヒントなしでファイルを再配置する必要があります。この問題の痛みを軽減するために現在のアプリで行ったことの 1 つは、ブックマークを保存するすべてのリソースに拡張属性を追加することです。少なくともこれを行うと、以前に関連付けられたリソースを検索するフォルダーをユーザーに選択させることができます。

苛立たしい制限がありますが、ブックマークは静的パスの保存に勝っています。

于 2014-08-11T16:01:36.820 に答える