feed

2024年02月18日, 編集履歴

macOS App Sandbox下におけるSecurity-scoped Bookmarkについて

App Sandbox環境下のmacOSアプリにおいて、ローカルファイルへのアクセスには制限が課せられている。

コード内で適当に生成したURLではローカルファイルへアクセスできない。ファイルへのアクセス権を得るには、

等の限られた方法を取る必要がある。

ただしNSOpenPanel等で取得したURLは、その起動中はURL先のファイルにアクセス可能だが、そのURLを保存しておいたとしても、次の起動時にはアクセスできなくなってしまう。URLへのアクセスを永続化するには、Security-scopedなURL Bookmarkを作らなければならない。

URLからSecurity-scoped Bookmarkの生成

URLbookmarkData(options:includingResourceValuesForKeys:relativeTo:)関数を使ってSecurity-scoped Bookmark(Data型)を作成する。options引数にはwithSecurityScope を指定。URLへのアクセスが読み込みのみで良い場合は、securityScopeAllowOnlyReadAccess も併用する。

let bookmarkData = try? url.bookmarkData(options: [.withSecurityScope, .securityScopeAllowOnlyReadAccess], includingResourceValuesForKeys: nil, relativeTo: nil)

Security-scoped BookmarkからURLを生成し、アクセスする

URLinit(resolvingBookmarkData:options:relativeTo:bookmarkDataIsStale:)関数を使うことで、Security-scoped BookmarkなDataからURLを生成できる。resolvingBookmarkData引数にはSecurity-scoped BookmarkなDataを、options引数にはwithSecurityScopeを指定する。

生成されたURLに実際にアクセスするには、アクセス前にstartAccessingSecurityScopedResource()を実行し、アクセス後にはstopAccessingSecurityScopedResource()を実行する。

var isStale = false
let urlData = // 前項の手法で保存したURLのData
if let url = try? URL(resolvingBookmarkData: urlData, options: .withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale) {
  if url.startAccessingSecurityScopedResource() {

    // URL先への読み書き処理

    url.stopAccessingSecurityScopedResource()
  }
}

URLinit(resolvingBookmarkData:options:relativeTo:bookmarkDataIsStale:)実行後にisStaletrueになっている場合、Bookmark Data作成以降にURL先ファイルの名前が変更されたり、場所が変更されたことを意味する。その場合でも返却されたURLは変更後のものになっており、アクセスは可能だが、新しくSecurity-scoped Bookmarkを生成し直す必要がある。

Security-scoped Bookmarkを再生成するときも、URL先へのアクセスに相当するので、startAccessingSecurityScopedResource()stopAccessingSecurityScopedResource()が必要になる。