6

我正在创建一个 TodayWidget 应用程序扩展,它显示有关用户在应用程序目录之外选择的文件夹的信息。

在我的主应用程序中,我可以通过 NSOpenPanel 使用 powerbox 来选择文件夹。然后,我可以将一个安全范围的书签保存到我的 TodayWidget 可访问的应用程序组容器的用户默认值中。

TodayWidget 可以读取书签数据,但是当它调用URLByResolvingBookmarkData时,它会出错:

The file couldn’t be opened because it isn’t in the correct format.

我的主应用程序和 TodayWidget 都具有以下权利:

  • com.apple.security.files.bookmarks.app-scope
  • com.apple.security.files.user-selected.read-only

从 Apple 的文档中,只有创建安全范围书签的应用程序才能使用它。我猜这些意味着不允许嵌入式应用程序?

我已经考虑过使用 XPC,但这并不能真正解决问题,因为 XPC 也不能使用安全范围的书签,只能使用普通书签。一旦计算机重新启动,XPC 进程将失去对目录的访问权限。

我真正需要的是一种让 XPC 进程获得对用户指定目录的读取访问权限的方法。有没有办法不必每次重新启动计算机都重新启动我的主应用程序?

4

2 回答 2

5

您可能已经解决了这个问题或继续前进。但是对于所有正在尝试类似事情的人,我会将其留给他们。为了在不同的应用程序中访问安全范围的书签,它们必须作为 NSData 传输并在其他应用程序中重新解析。

在我的例子中,我在主应用程序中显示一个打开的对话框,然后将作用域书签保存到共享NSUserDefaults套件中。其他应用程序也是该套件的一部分,然后访问NSData's 的容器并将它们解析为可用NSURL的 's

以下是相关的代码:

//Inside my main application's open function 
... get url from NSOpenPanel
BookmarkUtils.saveURLForOtherApplications(openPanel.URL!)


//Inside BookmarkUtils.swift
static func saveURLForOtherApplications(url:NSURL)->Bool{
    let defaults  = NSUserDefaults(suiteName: <#Suite-Name#>)!
    //I store them as a dictionary of path->encoded URL
    let sandboxedBookmarks:NSMutableDictionary
    if let prevBookmarks =  defaults.objectForKey(kSandboxKey) as? NSDictionary{
       sandboxedBookmarks = NSMutableDictionary(dictionary:prevBookmarks)
    }
    else{
        sandboxedBookmarks = NSMutableDictionary()
    }
    if let shareData = BookmarkUtils.transportDataForSecureFileURL(url){
        sandboxedBookmarks.setObject(shareData, forKey:url.path!)
        defaults.setObject(sandboxedBookmarks, forKey:kSandboxKey)
        defaults.synchronize()
        return true
    }
    else{
        println("Failed to save URL Data");
        return false
    }
}

static func transportDataForSecureFileURL(fileURL:NSURL)->NSData?{
    // use normal bookmark for encoding security-scoped URLs for transport between applications
    var error:NSError? = nil
    if let data =  fileURL.bookmarkDataWithOptions(NSURLBookmarkCreationOptions.allZeros, includingResourceValuesForKeys:nil, relativeToURL:nil, error:&error){
        return data;
    }
    else{
        println("Error creating transport data!\(error)")
        return nil
    }
}

所以然后在我的扩展中(在我的情况下是今天的视图)我做了这样的事情......

class TodayViewController: ...
    ...
    override func viewDidLoad() {
        super.viewDidLoad()

        var status = [MyCoolObjects]()
        for url in BookmarkUtils.sharedURLSFromApp(){
            BookmarkUtils.startAccessingSecureFileURL(url)
            status.append(statusOfURL(url))
            BookmarkUtils.stopAccessingSecureFileURL(url)
        }

    self.listViewController.contents = status
}

相关的书签看起来像:

static func sharedURLSFromApp()->[NSURL]{
    var urls = [NSURL]()
    if let defaults  = NSUserDefaults(suiteName: <#Suite-Name#>){
        if let prevBookmarks =  defaults.objectForKey(kSandboxKey) as? NSDictionary{
            for key in prevBookmarks.allKeys{
                if let transportData = prevBookmarks[key as! NSString] as? NSData{
                    if let url = secureFileURLFromTransportData(transportData){
                        urls.append(url)
                    }
                }
            }
        }
    }
    return urls
}



static func secureFileURLFromTransportData(data:NSData)->NSURL?{
    // use normal bookmark for decoding security-scoped URLs received from another application
    var bookmarkIsStale:ObjCBool = false;
    var error:NSError? = nil;
    if let fileURL = NSURL(byResolvingBookmarkData: data, options: NSURLBookmarkResolutionOptions.WithoutUI, relativeToURL: nil, bookmarkDataIsStale: &bookmarkIsStale, error: &error){
        return fileURL
    }
    else if(bookmarkIsStale){
        println("Bookmark was stale....")
    }
    else if let resolveError = error{
        println("Error resolving from transport data:\(resolveError)")
    }
    return nil
}

这个解决方案对我有用。一旦您解析了共享 URL,您就可以为该应用程序创建一个书签,并在需要时将其保存以备后用。可能有更好的方法,希望 Apple 能够解决这个问题,因为目前与扩展共享权限很痛苦。

于 2015-05-01T15:49:54.210 回答
0

实际上你不需要 startAccessingSecureFileURL 并且它会返回失败启动。只需将书签数据转换为 url 即可获得访问权限。

于 2016-12-09T08:32:27.280 回答