I have a FileProvider app consisting of MainApp and FileProviderExtension. MainApp has a popover which should have features like:
- Button which opens FileProvider folder
- List of files being uploaded/download and when user clicks on a file, it should either be opened or shown in Finder in containing folder.
How to achieve these in Sandboxed macOS app? I know it's possible because Microsoft's OneDrive app is distributed via AppStore and can perform these features.
So far I've been able to create an alias to a FileProvider folder in user's home folder by utilizing NSOpenPanel
.
I tried opening FileProvider files from MainApp with something like:
let url = try! await NSFileProviderManager(for: myDomain)?.getUserVisibleURL(for: fileIdentifier)
NSWorkspace.shared.open(url!)
but getUserVisibleURL
returns nil.
Does anyone know how to achieve described functionality?
I was chaising my own tail for a while. Anyways, here's a solution that worked for me:
Here is an example on how to open a FileProvider folder:
- Retrieve FileProvider root folder URL:
let fileProviderFolderURL = try! await NSFileProviderManager(for: SREG.context.domain)?.getUserVisibleURL(for: .rootContainer)
- Use
NSOpenPanel
to ask user where to store the symbolic link to FileProvider root folder. Then store bookmark data of user-selected location and create symbolic link to FileProvider root within that location.
let savePanel = NSOpenPanel()
savePanel.canChooseDirectories = true
savePanel.title = "Choose this location"
savePanel.prompt = "Choose"
let result = await savePanel.begin()
if result == NSApplication.ModalResponse.OK {
let panelURL = savePanel.url
let mountBookmarkData = try! panelURL.bookmarkData(options: .withSecurityScope)
UserDefaults.standard.set(mountBookmarkData, forKey: "aliasLocationBookmarkData")
let fileManager = FileManager.default
var aliasURL = panelURL.appendingPathComponent("MyRoot")
UserDefaults.standard.set(aliasURL.path, forKey: Constants.UserDefaults.aliasPath)
do {
try fileManager.createSymbolicLink(at: aliasURL, withDestinationURL: fileProviderFolderURL)
} catch {
// handle error
}
}
- Later, when you want to open the Root folder in Finder, fetch bookmark data from
UserDefaults
and create URL from it. It is the location where symbolic link is stored. Start secure access on it and then open the symbolic link URL:
let aliasLocationBookmarkData = UserDefaults.standard.data(forKey: "aliasBookmarkData")!
var isStale = false
let aliasLocationURL = try URL(resolvingBookmarkData: aliasLocationBookmarkData, options: .withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale)
let securityScopeResult = aliasLocationURL.startAccessingSecurityScopedResource()
if securityScopeResult {
let aliasPathKey = Constants.UserDefaults.aliasPath
let aliasPath = UserDefaults.standard.string(forKey: aliasPathKey)
let aliasURL = URL(fileURLWithPath: aliasPath)
NSWorkspace.shared.open(aliasURL)
aliasLocationURL.stopAccessingSecurityScopedResource()
}
That's pretty much it. The key is to store bookmark data of the location user picked in NSOpenPanel
dialog, create symbolic link in it and then you'll be able to access that resource even after app relaunch.