NSFileSandboxingRequestRelatedItemExtension: Failed to issue extension

Hi there,

I have an SwiftUI app that opens a user selected audio file (wave). For each audio file an additional file exists containing events that were extracted from the audio file. This additional file has the same filename and uses the extension bcCalls. I load the audio file using FileImporter view modifier and within access the audio file with a security scoped bookmark. That works well. After loading the audio I create a CallsSidecar NSFilePresenter with the url of the audio file. I make the presenter known to the NSFileCoordinator and upon this add it to the FileCoordinator. This fails with NSFileSandboxingRequestRelatedItemExtension: Failed to issue extension for; Error Domain=NSPOSIXErrorDomain Code=3 "No such process"

My Info.plist contains an entry for the document with NSIsRelatedItemType set to YES

I am using this kind of FilePresenter code in various live apps developed some years ago. Now when starting from scratch on a fresh macOS26 system with most current Xcode I do not manage to get it running. Any ideas welcome!

Here is the code:

struct ContentView: View {  
    @State private var sonaImg: CGImage?
    @State private var calls: Array<CallMeasurements> = Array()
    @State private var soundContainer: BatSoundContainer?
    
    @State private var importPresented: Bool = false
    
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text("Hello, world!")
            if self.sonaImg != nil {
                Image(self.sonaImg!, scale: 1.0, orientation: .left, label: Text("Sonagram"))
            }
            if !(self.calls.isEmpty) {
                List(calls) {aCall in
                    Text("\(aCall.callNumber)")
                }
            }
            Button("Load sound file") {
                importPresented.toggle()
            }
        }
        .fileImporter(isPresented: $importPresented, allowedContentTypes: [.audio, UTType(filenameExtension: "raw")!], onCompletion: { result in
            switch result {
            case .success(let url):
                let gotAccess = url.startAccessingSecurityScopedResource()
                if !gotAccess { return }
                if let soundContainer = try? BatSoundContainer(with: url) {
                    self.soundContainer = soundContainer
                    self.sonaImg = soundContainer.overviewSonagram(expectedWidth: 800)
                    
                    let callsSidecar = CallsSidecar(withSoundURL: url)
                    let data = callsSidecar.readData()
                    print(data)
                }
                url.stopAccessingSecurityScopedResource()
            case .failure(let error):
                // handle error
                print(error)
            }
        })
        .padding()
    }
}

The file presenter according to the WWDC 19 example:

class CallsSidecar: NSObject, NSFilePresenter {
    lazy var presentedItemOperationQueue = OperationQueue.main
    var primaryPresentedItemURL: URL?
    var presentedItemURL: URL?
    init(withSoundURL audioURL: URL) {
        primaryPresentedItemURL = audioURL
        presentedItemURL = audioURL.deletingPathExtension().appendingPathExtension("bcCalls")
    }
    func readData() -> Data? {
        var data: Data?
        var error: NSError?
        
        NSFileCoordinator.addFilePresenter(self)
        let coordinator = NSFileCoordinator.init(filePresenter: self)
        NSFileCoordinator.addFilePresenter(self)
        coordinator.coordinate(readingItemAt: presentedItemURL!, options: [], error: &error) {
            url in
            data = try! Data.init(contentsOf: url)
        }
        return data
    }
}

And from Info.plist

<key>CFBundleDocumentTypes</key>
	<array>
		<dict>
			<key>CFBundleTypeExtensions</key>
			<array>
				<string>bcCalls</string>
			</array>
			<key>CFBundleTypeName</key>
			<string>bcCalls document</string>
			<key>CFBundleTypeRole</key>
			<string>None</string>
			<key>LSHandlerRank</key>
			<string>Alternate</string>
			<key>LSItemContentTypes</key>
			<array>
				<string>com.apple.property-list</string>
			</array>
			<key>LSTypeIsPackage</key>
			<false/>
			<key>NSIsRelatedItemType</key>
			<true/>
		</dict>
		<dict>
			<key>CFBundleTypeExtensions</key>
			<array>
				<string>wav</string>
				<string>wave</string>
			</array>
			<key>CFBundleTypeName</key>
			<string>Windows wave</string>
			<key>CFBundleTypeRole</key>
			<string>Editor</string>
			<key>LSHandlerRank</key>
			<string>Alternate</string>
			<key>LSItemContentTypes</key>
			<array>
				<string>com.microsoft.waveform-audio</string>
			</array>
			<key>LSTypeIsPackage</key>
			<integer>0</integer>
			<key>NSDocumentClass</key>
			<string></string>
		</dict>
	</array>

Note that BatSoundContainer is a custom class for loading audio of various undocumented formats as well as wave, Flac etc. and this is working well displaying a sonogram of the audio.

Thx, Volker

I have a SwiftUI app that opens a user-selected audio file (wave). For each audio file, an additional file exists containing events that were extracted from the audio file. This additional file has the same filename and uses the extension bcCalls. I load the audio file using FileImporter view modifier and then access the audio file with a security-scoped bookmark. That works well.

To clarify here, do you mean that you’re able to access both the directly selected file and its related sidecar? Or just that you're able to access the direct file?

After loading the audio, I create a CallsSidecar NSFilePresenter with the URL of the audio file. I make the presenter known to the NSFileCoordinator and then add it to the FileCoordinator. This fails with NSFileSandboxingRequestRelatedItemExtension: Failed to issue extension for; Error Domain=NSPOSIXErrorDomain Code=3 "No such process"

Is the failure here about opening an existing file or about writing a new file? Also, what directory are you working out of?

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Hello Kevin,

thx for coming here! Let me clarify:

I can only access the user selected file. The sidecar file can not be accessed, due to not getting a FilePresenter (Sandbox tells failed to issue an extension for file)

The files both user selected and sidecar reside in a folder inside my users document folder and I try to read the sidecar when the user opens the audio file.

I can access the sidecar file from terminal and load its content. I can also load its content when opening it with a second NSOpenPanel(). So access rights are okay. Using one of my Apps from the AppStore which implements the same file access works as well.

So currently I am lost why my fresh code on the fresh installed machine is not working.

TIA, Volker

The files, both user-selected and sidecar, reside in a folder inside my user’s document folder, and I try to read the sidecar when the user opens the audio file.

OK. SO, one thing I would test here is what happens if you test the same code with files that are in a directory "outside" of the normal user hierarchy. You can try with a directory inside your home directory, but I actually suggest using a folder ENTIRELY outside the user hierarchy— for example, on a disk image or an external volume.

The issue here is that macOS has multiple independent systems of data protection, most of which are focused on "user data" (like the contents of "~/Documents/"). Moving outside of that hierarchy means you’re looking at the "basic" case, which is easier to evaluate.

Looking at the other cases here:

I can access the sidecar file from the terminal and load its content.

FYI, Terminal.app is a tricky tool to test an experiment with. The issues here are:

  1. It's not sandboxed, which exempts it from the more aggressive file protection you’re dealing with here. This is mostly to avoid making you manually select which directories it has access to or using an entitlement to directly grant it broad access. However, this also means that it doesn't bypass the non-sandbox protections, which does have some security benefit.

  2. It's very easy to forget what actual access you've already given it. For example, it's very likely that you can list the contents of ~/Downloads, but that isn’t because it has special access. What actually happened is that at some previous point (weeks/months/years before) you tried accessing the directory and then approved the "Terminal.app is trying to access <directory>" dialog, which allowed later access. Many developers also grant it FDA (Full Disk Access), which bypasses most of these issues.

So access rights are okay. Using one of my apps from the App Store which implements the same file access works as well.

Was the App Store app the same app that created the sidecar file? I haven't looked into the details of this, but the quarantine data attached to the file also tracks what app(s) it interacts with, and we may be using that to validate access.

So currently I am lost as to why my fresh code on the fresh installed machine is not working.

So, what's tricky here is that there are lots of different protection mechanisms in place (see this post by Quinn for a good overview), which can make it very tricky to determine what's actually failing and why. The first thing I would actually do before spending too much time here is testing again with Developer ID signed build installed in "/Applications/". That's a "shipping" use case (which is what actually matters), so if that works, then you don't necessarily need to spend a lot of time figuring out EXACTLY what's going wrong.

Finally, on this point:

I can also load its content when opening it with a second NSOpenPanel().

First off, I think it's always a good idea to have a "backup" path in place for your users. It's very difficult to guarantee that something like this will "always" work, so the open panel serves as your backup plan to ensure that the user can always get things working no matter what happens.

In a similar vein, I'd also think about how you can tweak the user experience to sidestep these issues. For example, if you configure your sidecar files to be openable by your app, and your sidecar files include a document-scoped bookmark to its related file, then opening the sidecar gets you both files without relying on any special support from the system.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

NSFileSandboxingRequestRelatedItemExtension: Failed to issue extension
 
 
Q