NSSavePanel Crashes on Big Sur for public.csv UTI When You Don't Have a CSV Editor

Was working on an export sheet today and during testing on Big Sur, I noticed that the button that would bring up the save panel would produce a beachball for a while, and then I’d get an error dialog telling me that the panel crashed.

I changed the NSSavePanel.allowedFileTypes property to ["public.csv"] during this update, and that trips up the save panel on Big Sur. Note that ["csv"] works just fine.

It seems to be related to the file types being declared as an array of UTI (Universal Type Identifiers). This worked marvellous thus far, so I’m a bit confused about the crash.

From the header files:

NSSavePanel: An array of NSStrings specifying the file types the user can save the file as. The file type can be a common file extension, or a UTI.

Looking at the docs and header files, it looks like allowedFileTypes was deprecated in favor of macOS 11.0 API, allowedContentTypes. That might explain it, but it doesn’t excuse the backwards-incompatibility.

Actually, the UTI recommendation is not true anymore.

By now, I expected NSSavePanel.alloedContentTypes to work, and then to have Xcode suggest to wrap access to that property in an if-@available block. But that doesn’t work at all. With macOS 10.15 Catalina being my main dev machine, I cannot use the new API at all at the moment, it seems, no matter what I set the deployment target to. I’m using Xcode 12.3 by the way.

Meanwhile, a save panel using UTI file types works fine in TableFlip on Big Sur. I don’t know why. (I’m happy that it wasn’t broken for the past months, though.)

When I actually ran TableFlip to test this, which is an NSDocument-based app, and then tried to run the breaking sample app again, everything workes fine.

Wat?!

Deleting TableFlip from the Big Sur test device produced the error again.

It actually turns out that public.csv is not a built-in file type recognized by macOS. The archived docs for UTIs list many UTIs, but not CSV.

So make sure to check your assumptions when you write apps that export data without actually registering the exported file type UTIs!

Update 2021-01-21: Thanks Aaron Tuller for pointing me to kUTTypeCommaSeparatedText which has the value "public.comma-separated-values-text". That might work!

As for CSV, my personal recommendation is to make sure you register the UTI with your app and not pass file extensions instead of UTIs: in Xcode’s target settings for the app target, go to “Info” and fill in the blanks for a new UTI below “Exported Imported Type Identifiers”:

Example of how I fill in the blanks to register CSV

Update 2021-01-21: Thanks to Nate Weaver for pointing out that I should use an Imported and not an Exported Type Identifier. Here’s why, from the docs:

An imported UTI declaration is used to declare a type that the bundle does not own, but would like to see available on the system

I filed FB8974625 for the crash due to stack overflow in a self-referencing method call in AppKit’s save panel.

I don’t have a virgin Catalina computer lying around, so I cannot test if this is an old problem or just related to Big Sur. If you know of this problem on pre-Big Sur machines, let me know in the comments!