SwiftUI on macOS equivalent of NSSavePanel for choosing a destination URL?

In AppKit, NSSavePanel can be used to ask the user for a destination URL before the app creates a file.

What is the SwiftUI equivalent for this?

.fileImporter covers the NSOpenPanel case well enough, but I have not found a SwiftUI API that matches the simple NSSavePanel case where the app only needs the URL.

There's a new API in .fileExporter that appears close, but it requires a non nil WritableDocument, and seems designed around SwiftUI performing the file export.

My use case is a macOS app that creates new documents backed by SQLite. SQLite needs a file path so it can create the database at that location. With NSSavePanel, I can ask the user where to save the document, receive a URL, and then create the SQLite database myself.

Is there a SwiftUI API for this on macOS 26 or later? If not, is NSSavePanel still the recommended approach for this case?

Answered by DTS Engineer in 895966022

Thanks for the clear write-up. Your reading is correct.

As of macOS 26, SwiftUI's file modifiers are fileImporter (open, returns URLs), fileExporter (writes a document or Transferable item you supply), and fileMover (moves an existing file). None of them return a bare destination URL the way NSSavePanel does, so there is no SwiftUI equivalent for choosing a save location and getting a URL back. Presenting .fileExporter with an empty placeholder document only to read its URL writes an empty file and works against how the modifier operates, so it is not a substitute.

NSSavePanel is the supported way to get that destination URL, and using it from a SwiftUI app is a normal use of AppKit, not a workaround. You present it from your SwiftUI action, read panel.url, and pass that URL to your database. Since you create the file at that location, the sandbox behavior is relevant: the NSSavePanel documentation notes that when someone saves through the panel, macOS adds the chosen file to your app's sandbox, so the URL is writable without additional entitlement work.

If you run into anything specific while wiring the returned URL to your database, I am glad to talk about that.

Thanks for the clear write-up. Your reading is correct.

As of macOS 26, SwiftUI's file modifiers are fileImporter (open, returns URLs), fileExporter (writes a document or Transferable item you supply), and fileMover (moves an existing file). None of them return a bare destination URL the way NSSavePanel does, so there is no SwiftUI equivalent for choosing a save location and getting a URL back. Presenting .fileExporter with an empty placeholder document only to read its URL writes an empty file and works against how the modifier operates, so it is not a substitute.

NSSavePanel is the supported way to get that destination URL, and using it from a SwiftUI app is a normal use of AppKit, not a workaround. You present it from your SwiftUI action, read panel.url, and pass that URL to your database. Since you create the file at that location, the sandbox behavior is relevant: the NSSavePanel documentation notes that when someone saves through the panel, macOS adds the chosen file to your app's sandbox, so the URL is writable without additional entitlement work.

If you run into anything specific while wiring the returned URL to your database, I am glad to talk about that.

@DTS Engineer

Thanks for the quick follow-up. I'm pretty familiar with what you've presented. The "problem" with invoking NSSavePanel from within a SwiftUI macOS application is that you don't necessarily have access to the underlying NSWindow to properly present the NSSavePanel as a sheet, which would be the expected user experience.

I'm aware of the following ways to work around this, all of which feel awkward in a pure SwiftUI environment:

  • Assume the application's current key window is the one to use.
  • Use an NSViewRepresentable as a "window accessor"
  • Abandon the full SwiftUI app lifecycle and just using a traditional AppKit lifecycle for window management.

Are there other ways I've overlooked that are more idiomatic SwiftUI?

The ability to ask the user for a URL to save content to is such a fundamental macOS feature that it's omission genuinely perplexes me.

Are you able to elaborate at all as to what the general rational and thinking is behind this?

Thanks for writing back. You may already be doing this, in which case take it as confirmation rather than news: NSSavePanel does not need an NSWindow to present at all.

Calling panel.runModal() from your SwiftUI action presents the panel app-modally and returns the chosen URL, with no window reference involved. That keeps you entirely within the SwiftUI app lifecycle, and it avoids the window question entirely. The tradeoff is that the panel appears as a standalone modal dialog rather than as a sheet attached to your window.

The window only matters if you specifically want that document-modal sheet, through beginSheetModal(for:). That path does need the hosting NSWindow, and as of macOS 26 SwiftUI does not expose a first-class accessor for it. If your app has a single window, NSApp.keyWindow is a perfectly good way to get it. The ambiguity only arises once more than one window can be key; for that case, a small NSViewRepresentable that reads its view's window is the more robust idiom. Either way, dropping the SwiftUI app lifecycle for an AppKit one is not necessary: neither runModal(), NSApp.keyWindow, nor the NSViewRepresentable accessor requires it.

Either path is supported: runModal() for an app-modal panel, or beginSheetModal(for:) for the sheet, getting the window from NSApp.keyWindow (single window) or an NSViewRepresentable (multiple windows).

If a SwiftUI-native way to request a save destination would fit your app better, that is a good thing to send through Feedback Assistant, which puts it directly in front of the team that owns these APIs.

Please let me know if there are any questions.

SwiftUI on macOS equivalent of NSSavePanel for choosing a destination URL?
 
 
Q