Supported read/write handoff of a sandbox-container PNG to Preview using NSWorkspace

Hello,

I am preparing an existing macOS screenshot utility for distribution through the Mac App Store.

The app stores captured PNG files under its temporary directory:

FileManager.default.temporaryDirectory .appendingPathComponent("ShelfDragPoC/Captures", isDirectory: true)

A context-menu "Edit" action passes the original PNG URL to Preview using NSWorkspace. The app does not create an editing copy.

Relevant code:

import AppKit

@MainActor func openInPreview(_ url: URL) { guard let previewURL = NSWorkspace.shared.urlForApplication( withBundleIdentifier: "com.apple.Preview" ) else { return }

let configuration = NSWorkspace.OpenConfiguration()
configuration.activates = true

NSWorkspace.shared.open(
    [url],
    withApplicationAt: previewURL,
    configuration: configuration
) { _, _ in }

}

The app monitors the original PNG URL for file changes and refreshes its thumbnail when changes are detected.

Observed behavior:

  1. In the existing non-sandboxed app, rotating an image in Preview updates the original PNG and the thumbnail without requiring Command-S.

  2. In the sandboxed validation app, Preview opens the image, but editing results in a separate copy.

  3. When closing the edited document, Preview displays a message indicating that the original document will not be changed.

  4. The original PNG and the app's thumbnail are not updated.

The Preview launch code and file watcher are unchanged between the non-sandboxed and sandboxed versions.

The sandboxed app has these entitlements:

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

The app-scoped bookmark is used for a separate Desktop export feature. The PNG opened in Preview remains in the app's sandbox container.

My question:

Is there a supported public API that allows a sandboxed Mac App Store app to open its own container-resident PNG in Preview with the permissions required for in-place editing and normal automatic saving?

Ideally, the user would only need to select "Edit" once, without an additional file-selection dialog or an explicit Save command.

We want to preserve the original PNG URL, keep App Sandbox enabled, and avoid automatically exporting temporary images to the Desktop.

If this workflow is not supported, what is Apple's recommended approach?

Thank you.

Answered by DTS Engineer in 915650022
Written by SuzuyaKoboDev in 915396022

This is consistent with your safe-save hypothesis

Yep.

Written by SuzuyaKoboDev in 915396022

although it does not establish the underlying cause.

Agreed.

Written by SuzuyaKoboDev in 915396022

I can include this successful comparison alongside the failing cases in the Preview bug report you recommended.

Thanks.

But I think it also gives you a path to a workaround:

  1. Rather than save these temporary PDFs PNGs in your container, ask the user to select a directory for PDF PNG sharing.
  2. Use a security-scoped bookmark to persist access to that directory.
  3. When it comes time to share a PDF PNG, write it to that directory.
  4. And then open that URL with NSWorkspace.

How’s that sound?

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

It's always risky trying to integrate with Apple's own apps.

Plus, in this modern age of paranoia, security restrictions may impact Apple apps as well as 3rd party apps. This file is in your app's container. I don't think what you're trying to accomplish is documented in any way. The only documentation and API support is to prevent the action you describe.

I think there are multiple other APIs to get data (or "document data") from one app to another. But then monitoring the other side is, at best, risky, and often flat-out impossible. The user can always rename or save elsewhere. If your app is claiming it can monitor that activity, the user is going to blame your app when it fails.

Then there's a more existential issue. You're essentially unhorsing yourself. You want users to use your app. The last thing you should ever do it launch someone else's app, even Apple's. It's your app. That's your user. Keep them in your app. Maybe you can use PDFKit and do those same operations totally inside your app.

This is going to be tricky. There are multiple potentially conflict file system permissions constraints in play here. I talk about those in On File System Permissions.

I’d like to clarify this bit of your question:

Written by SuzuyaKoboDev in 852014021

The app stores captured PNG files under its temporary directory:

Are you talking about the actual temporary directory, as returned by the temporaryDirectory property, or the older NSTemporaryDirectory()? Or a directory you manage that just happens to contain files that you consider temporary?

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi Quinn,

Thank you for looking into this.

It is A: FileManager.default.temporaryDirectory.

The app creates a subdirectory beneath the URL returned by that API:

FileManager.default.temporaryDirectory
    .appendingPathComponent("ShelfDragPoC/Captures", isDirectory: true)

The captured image is saved as a PNG using Data.write(to:options: .atomic), and the resulting URL is passed to Preview via NSWorkspace.open(_:withApplicationAt:configuration:completionHandler:).

The minimal reproduction project uses the same API and write method, with a different subdirectory (PreviewSandboxRepro/Captures) and a synthetic PNG.

Neither implementation explicitly calls NSTemporaryDirectory() or uses a separately chosen temporary-directory root.

Thank you!

Thanks for that extra info.

I’d like to tease apart sandbox restrictions from other restrictions associated with the temporary directory. If you put this PDF PNG in a normal area of your app’s container, does that change the behaviour? For example, if you FileManager.default.temporaryDirectory to this:

let dir = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)

and thus put the PDF PNG in your app’s Documents directory.

I’m not suggesting that this is an actual solution that you ship to customers; rather, it’s just a diagnostic test to see whether the temporary directory is part of this issue.

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi Quinn,

I tested both locations with synthetic PNGs on macOS 26.6.2 (25G83), using Preview 11.0 (1113.5.3).

A used FileManager.default.temporaryDirectory.

B used FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true).

I confirmed that B resolved to the app container's Documents directory, not the user's ordinary Documents folder.

Both cases used the same Apple Development-signed sandboxed app, identical initial PNG bytes, atomic writing, and the same NSWorkspace open call. The storage root, distinguishing A/B filename labels, and creation times differed.

For each PNG, I selected Rotate Left once in Preview without pressing Command-S, waited at least 10 seconds, and inspected the original file.

In both cases, Preview changed the document title to a copy. Both originals remained 640 × 360 pixels and 10,684 bytes, with unchanged SHA-256 and modification timestamps.

When closing B, Preview asked whether to keep the new copy and stated that the original document would not be changed. I cancelled without saving a separate copy.

The signed app has these three entitlements, all set to true:

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

Moving the PNG to the container's Documents directory did not change the observed behavior.

What would you recommend checking next?

Thank you!

Thanks for running that test. I have one more test for you to try: Rather than opening a PNG with Preview, try opening a text file (.txt) with TextEdit.

I’m suggesting this because, now that we’ve ruled out the temporary directory as a factor, I’d next like to rule out Preview.

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi Quinn,

I completed the TextEdit comparison on macOS 26.6.2.

Both tests used the same Apple Development-signed sandboxed app and the same NSWorkspace opening method.

  • A (temporaryDirectory): The original text file was updated successfully after editing and pressing Command-S. Its size changed from 33 to 40 bytes.
  • B (container Documents): The original text file was also updated successfully. Its size changed from 33 to 39 bytes.

In both cases, the original file's content, SHA-256, and modification time changed. The one-byte difference is due to a trailing newline in A.

Unlike the earlier PNG/Preview tests, TextEdit successfully saved changes to the original files in both locations.

The earlier Preview test used Rotate Left without explicitly pressing Command-S, whereas this TextEdit test used Command-S.

This suggests that the temporary directory alone does not explain the issue, although the file type, application, and save behavior also differ.

What would you recommend investigating next?

Thank you!

Written by SuzuyaKoboDev in 914219022

TextEdit successfully saved changes to the original files in both locations.

Interesting. I thought that might be the case, which is why I set you off on this path (-:

Here’s my theory as to what’s happening…

When you open a document using NSWorkspace, the system issues a sandbox extension to the receiving process [1]. That grants the receiving process the ability to read and write that file. But only that file. It doesn’t grant access to the surrounding directory.

When apps modify a document they typically use a safe-save strategy. That is, they save the modified content to a different file and then atomically replace the original file with that new content.

There are various ways an app can implement its safe-save strategy. It seems that TextEdit’s one is compatible with your sandbox constraints and Preview’s one isn’t.

I’d label this as a bug in Preview, and I encourage you to file it as such. And if you do file a bug, please post your bug number, just for the record.

However, a fix for that is going to take time, which raises the question of what to do in the meantime. And I can’t think of a great way around this. There’s no location on the file system where a sandboxed app can save files without user interaction that are then accessible to other, unrelated [2], sandboxed apps without user interaction.

I have one last option that I’d like you to try:

  1. In your app, present an open panel that lets the user choose a directory for these temporary PNGs.
  2. And update your code to save your PDF PNG to that directory.
  3. Then run the app and choose a directory that’s not special in any way. You might, for example, create a Test directory in your home directory and choose that.

How does Preview behave with this arrangement?

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] I discuss the concept of sandbox extension in the On File System Permissions post that I referenced earlier.

[2] So, apps not from the same team. Apps from the same team can share files using an app group container.

Hi Quinn,

Thank you for your patience and continued guidance. I’ve completed the user-selected-folder test on macOS 26.6.2 with Preview 11.0.

The signed sandboxed sample used NSOpenPanel to select an ordinary local Test folder outside its container, wrote the synthetic PNG directly there, and opened the original URL in Preview through NSWorkspace. The same three sandbox entitlements were retained. The app held access to the selected folder during the test and did not create an editing copy.

After one counterclockwise rotation, I waited more than 10 seconds without pressing Command-S. The original PNG changed from 640 × 360 / 10,684 bytes to 360 × 640 / 12,243 bytes. Its SHA-256 and modification time also changed.

I then pressed Command-S once. No Save As dialog appeared. The dimensions, size, and SHA-256 remained unchanged from the rotated result, while the modification time changed again.

Thus, the original PNG was updated in the selected folder even before explicit Save. In contrast, the earlier temporaryDirectory and container Documents tests produced copies after rotation without Command-S. The TextEdit tests succeeded in both locations with Command-S.

This is consistent with your safe-save hypothesis, although it does not establish the underlying cause. I can include this successful comparison alongside the failing cases in the Preview bug report you recommended.

Thank you again for your help.

Best regards,
SuzuyaKoboDev

Written by SuzuyaKoboDev in 915396022

This is consistent with your safe-save hypothesis

Yep.

Written by SuzuyaKoboDev in 915396022

although it does not establish the underlying cause.

Agreed.

Written by SuzuyaKoboDev in 915396022

I can include this successful comparison alongside the failing cases in the Preview bug report you recommended.

Thanks.

But I think it also gives you a path to a workaround:

  1. Rather than save these temporary PDFs PNGs in your container, ask the user to select a directory for PDF PNG sharing.
  2. Use a security-scoped bookmark to persist access to that directory.
  3. When it comes time to share a PDF PNG, write it to that directory.
  4. And then open that URL with NSWorkspace.

How’s that sound?

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi Quinn,

Thank you for suggesting this workaround. It sounds promising, and I think it could be a practical solution for my app.

My plan is to ask the user to select a folder once, persist access using a security-scoped bookmark, and reuse that folder for subsequent screenshots without requiring the user to select it each time.

I would like to verify that the bookmark can be resolved after relaunching the sandboxed app and that Preview can still update the original PNG through NSWorkspace.

My app works with PNG screenshots rather than PDFs, but I believe the same approach applies.

I'll test this in a separate sample before making any changes to the production app.

Thank you again for your continued guidance.

Best regards,
SuzuyaKoboDev

Written by SuzuyaKoboDev in 915832022

My app works with PNG screenshots rather than PDFs

Right, sorry. I keep going back to the idea that this is about PDFs, which it isn’t. I’ve fixed all my mistakes above and hopefully that’ll keep me on track going forward.

Written by SuzuyaKoboDev in 915832022

I'll test this in a separate sample

Cool. Lemme know how it goes because this has been an interesting thread.

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"