Portrait

I2H3

Notes from work as an app developer for iPhones, iPads and Macs.

RSS Icon GitHub Mastodon

FileManager Throws Error on Getting File Provider Services

I found out how to successfully get replicated file provider services by URL on iOS which previously was impossible due to FileManager throwing a cryptic error.

This has been a blocker since weeks now. One of those strange issues nobody else has on the web. Nobody I asked had an idea what it could be about. I was so clueless that I feared the iOS port of my project is in danger.

I reported a feedback at Apple and as in most cases did not hear back. Except for them telling me the attachment uploaded by macOS Feedback Assistant was 0 bytes in size. In response I uploaded the example project again and it worked.

Anyway, when you want to resolve the custom services exposed by your file provider extension from your main app, then you have to call this at some point:

let url = try await manager.getUserVisibleURL(for: .rootContainer)
try await FileManager.default.fileProviderServicesForItem(at: url)

On macOS, this always worked fine. On iOS, it did not. The getFileProviderServicesForItem(at:) call always threw an error. Something cryptic and basic like a file could not be saved. I was puzzled a long time. I checked signing, identifiers and entitlements a dozen times, everything I could think of in context of sandboxing. Especially because we had another project in which it worked reliably on iOS. The only difference was that the latter was still implementing a non-replicated file provider extension.

Then, today, when I was using a convenience feature of a SwiftUI view. It is a button which reveals the file provider domain Finder. It also fetches the user-visible URL of the root container of a file provider domain and then calls this:

NSWorkspace.shared.activateFileViewerSelecting([url])

I noticed warnings in the Xcode console that the URL was not inside the app’s sandbox. It turned out that I have to call .activateFileViewerSelecting(_:) first.

if url.startAccessingSecurityScopedResource() {
    NSWorkspace.shared.activateFileViewerSelecting([url])
    url.stopAccessingSecurityScopedResource()
} else {
    logger.error("Failed to access file provider domain URL!")
}

It resolved the problem and I immediately though of the problem with the replicated file provider service lookup on iOS. And guess what? It is the solution there, too!

a memoji

The .fileProviderServicesForItem(at:) call must be enclosed security scoped URL access, too, like this:

if url.startAccessingSecurityScopedResource() {
    services = try await FileManager.default.fileProviderServicesForItem(at: url)
    url.stopAccessingSecurityScopedResource()
} else {
    logger.error("File manager threw an error while fetching services for item at URL \"\(url.absoluteString)\"!")
    throw FileProviderProxyError.failedToFindService
}

With this in place I could run my replicated file provider extension on iOS without a single code change.

a memoji