macOS如果想上苹果市场发布的话,那么必须要遵守苹果的沙盒协议,这样应用的存储默认都是沙盒路径,隔离了用户的文件系统,那么这个时候我需要访问 /User/xxx/Library/Developer/ 这种文件夹的时候,直接访问是会被拒绝的,那既然这样就肯定要授权了,等同意了在访问就名正言顺了。
那么要如何进行这种操作呢,首先想到的肯定是先把这个文件夹打开再说,然后当次的权限就有了,然后就是解决后续依然有权限访问的问题,这个时候就用到了 bookmark,这个东东就是转门干这个用的,下面一步一步的来实现这个功能。
本例子以授权 /user/xxx/Library/Developer/ 路径为例。
func getAbsolutePath(path: String) -> String? {
let pw = getpwuid(getuid())
guard let home = pw?.pointee.pw_dir else {
return nil
}
let homePath = FileManager.default.string(withFileSystemRepresentation: home, length: Int(strlen(home)))
return "\(homePath)/\(path)"
}
let path = getAbsolutePath("/Library/Developer")
// /User/xxx/Library/Developer/
func openFinder() {
let fm = FileManager.default
let filepath = getAbsolutePath(path: "Library/Developer/")!
print("filepath is \(filepath)")
let url = URL(filePath: filepath)
let dialog = NSOpenPanel()
dialog.message = "文件夹授权以提供服务"
dialog.prompt = "选择"
dialog.allowsOtherFileTypes = false
dialog.canChooseFiles = false
dialog.canChooseDirectories = true
dialog.directoryURL = url
dialog.begin { response in
if response == .OK, let folderPath = dialog.url {
// 可以判断一下是不是想要的文件夹,这个时候已经有权限了。
// 保存书签权限
}
}
}
把书签数据保存在bookmark中,以后用的时候再取出来用。
private func saveBookmarkData(for workDir: URL) {
do {
let bookmarkData = try workDir.bookmarkData(options: .withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil)
print("save book mark data")
// save in UserDefaults
UserDefaults.standard.setValue(bookmarkData, forKey: "bookmark")
} catch {
print("Failed to save bookmark data for \(workDir)", error)
}
}
private func restoreFileAccess() -> Bool {
do {
if let bookmark = UserDefaults.standard.object(forKey: "bookmark") as? Data {
var isStale = false
let url = try URL(resolvingBookmarkData: bookmark, options: .withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale)
guard url.startAccessingSecurityScopedResource() else {
return false
}
return true
}
return false
} catch {
print("Error resolving bookmark:", error)
return false
}
}
以下为完整代码
import SwiftUI
struct ContentView: View {
@State var fileSize: Int = 0
@State var canAccess: Bool = false
func getAbsolutePath(path: String) -> String? {
let pw = getpwuid(getuid())
guard let home = pw?.pointee.pw_dir else {
return nil
}
let homePath = FileManager.default.string(withFileSystemRepresentation: home, length: Int(strlen(home)))
return "\(homePath)/\(path)"
}
private func saveBookmarkData(for workDir: URL) {
do {
let bookmarkData = try workDir.bookmarkData(options: .withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil)
print("save book mark data")
// save in UserDefaults
UserDefaults.standard.setValue(bookmarkData, forKey: "bookmark")
} catch {
print("Failed to save bookmark data for \(workDir)", error)
}
}
private func restoreFileAccess() -> Bool {
do {
if let bookmark = UserDefaults.standard.object(forKey: "bookmark") as? Data {
var isStale = false
let url = try URL(resolvingBookmarkData: bookmark, options: .withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale)
guard url.startAccessingSecurityScopedResource() else {
return false
}
return true
}
return false
} catch {
print("Error resolving bookmark:", error)
return false
}
}
func openFinder() {
let fm = FileManager.default
let filepath = getAbsolutePath(path: "Library/Developer/")!
print("filepath is \(filepath)")
let url = URL(filePath: filepath)
let dialog = NSOpenPanel()
dialog.message = "Choose your directory"
dialog.prompt = "Choose"
dialog.allowsOtherFileTypes = false
dialog.canChooseFiles = false
dialog.canChooseDirectories = true
dialog.directoryURL = url
dialog.begin { response in
if response == .OK, let folderPath = dialog.url {
saveBookmarkData(for: folderPath)
}
}
}
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("占用容量: \(fileSize)")
Button {
if !canAccess {
openFinder()
}
} label: {
if canAccess {
Text("已授权")
} else {
Text("授权")
}
}
}
.padding()
.onAppear {
if restoreFileAccess() {
canAccess = true
} else {
canAccess = false
}
print("access is \(canAccess)")
}
}
}
#Preview {
ContentView()
}