Associating file extension with my application

My application uses a text file with an extension of .dssfilelist. On Linux I would register the Mime type and associate it with the application in the .desktop file.

<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
    <mime-type type="text/dssfilelist">
        <comment>DeepSkyStacker file-list file</comment>
        <glob pattern="*.dssfilelist" />
    </mime-type>
</mime-info>

I believe that I need to add stuff to the Info.plist for my application, but I also understand that CFBundleTypeExtensions is deprecated.

So please could you show me what I now need to add to the Info.plist file so that these files will be registered as "text/dssfilelist" type and associated with my application and to associate a .icns file with it?

Answered by DTS Engineer in 838587022

The best way to deal with this is in Xcode. Specifically, if you bring up the target editor for an app target, the Info tab has slices labelled Document Types, Exported Type Identifiers, and Imported Type Identifiers.

My advice above still stands even if you’re not using Xcode. In that case I recommend that you use Xcode to set things up how you want them, then look at the resulting Info.plist to see what it did.

Another good trick is to look at how other apps do this. I’ve included an example of that below.

Current documentation for this stuff is in Bundle Resources > Information Property List > Data and storage. That’s very… well… focused, which is why I tend to lean into Xcode. If you’re looking for more conceptual stuff, the legacy docs are generally OK. This stuff hasn’t changed significantly since it was introduced.

Share and Enjoy

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


% plutil -p /Applications/Pages.app/Contents/Info.plist          
{
  …
  "CFBundleDocumentTypes" => [
    0 => {
      "CFBundleTypeName" => "com.apple.iwork.pages.document"
      "CFBundleTypeRole" => "Editor"
      "LSDefaultShareModeCollaboration" => 1
      "LSHandlerRank" => "Owner"
      "LSItemContentTypes" => [
        0 => "com.apple.iwork.pages.pages"
      ]
      "NSDocumentClass" => "TPMacDocument"
      "NSDownloadsUbiquitousContents" => 1
      "NSIsRelatedItemType" => 1
      "NSUbiquitousDocumentUserActivityType" => "com.apple.pages.documentEditing"
    }
    …
  ]
  …
  "UTExportedTypeDeclarations" => [
    0 => {
      "UTTypeConformsTo" => [
        0 => "com.apple.package"
        1 => "public.composite-content"
      ]
      "UTTypeDescription" => "Pages Document"
      "UTTypeIcons" => {
        "UTTypeIconText" => "PAGES"
      }
      "UTTypeIdentifier" => "com.apple.iwork.pages.pages-tef"
      "UTTypeTagSpecification" => {
        "public.filename-extension" => [
          0 => "pages-tef"
        ]
      }
    }
    …
  ]
  "UTImportedTypeDeclarations" => [
    0 => {
      "UTTypeConformsTo" => [
        0 => "public.data"
        1 => "public.composite-content"
      ]
      "UTTypeDescription" => "Microsoft Word Macro-Enabled template"
      "UTTypeIdentifier" => "org.openxmlformats.wordprocessingml.template.macro-enabled"
      "UTTypeTagSpecification" => {
        "com.apple.ostype" => [
          0 => "DOTM"
        ]
        "public.filename-extension" => [
          0 => "dotm"
        ]
      }
    }
    …
  ]
}

PS where can I find the information about the Info.plist file in the current documentation - contents doesn't suggest any obvious place to look... I looked under the XCode section but didn't find what I wanted.

Thanks David

That is in the archive, so isn't current ...

I think that having something like this in the Info.plist file is part of the story

<key>UTExportedTypeDeclarations</key>
<array>
	<dict>
		<key>UTTypeIdentifier</key>
		<string>com.github.deepskystacker-dssfilelist</string>
		<key>UTTypeConformsTo</key>
		<array>
			<string>public.plaintext</string>
		</array>
		<key>UTTypeDescription</key>
		<string>DeepSkyStacker file-list file</string>
		<key>UTTypeIconFile</key>
		<string>DSS_filelist.icns</string>
		<key>UTTypeTagSpecification</key>
		<dict>
			<key>public.filename-extension</key>
			<array>
				<string>dssfilelist</string>
			</array>
		</dict>
		<key>public.mime-type</key>
		<string>text/dssfilelist</string>
	</dict>
</array>

Is that correct? If there are errors please tell me. Will that also cause d'clicking on a .dssfilelist file cause my application com.github.deepskystackerto open it?

The best way to deal with this is in Xcode. Specifically, if you bring up the target editor for an app target, the Info tab has slices labelled Document Types, Exported Type Identifiers, and Imported Type Identifiers.

My advice above still stands even if you’re not using Xcode. In that case I recommend that you use Xcode to set things up how you want them, then look at the resulting Info.plist to see what it did.

Another good trick is to look at how other apps do this. I’ve included an example of that below.

Current documentation for this stuff is in Bundle Resources > Information Property List > Data and storage. That’s very… well… focused, which is why I tend to lean into Xcode. If you’re looking for more conceptual stuff, the legacy docs are generally OK. This stuff hasn’t changed significantly since it was introduced.

Share and Enjoy

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


% plutil -p /Applications/Pages.app/Contents/Info.plist          
{
  …
  "CFBundleDocumentTypes" => [
    0 => {
      "CFBundleTypeName" => "com.apple.iwork.pages.document"
      "CFBundleTypeRole" => "Editor"
      "LSDefaultShareModeCollaboration" => 1
      "LSHandlerRank" => "Owner"
      "LSItemContentTypes" => [
        0 => "com.apple.iwork.pages.pages"
      ]
      "NSDocumentClass" => "TPMacDocument"
      "NSDownloadsUbiquitousContents" => 1
      "NSIsRelatedItemType" => 1
      "NSUbiquitousDocumentUserActivityType" => "com.apple.pages.documentEditing"
    }
    …
  ]
  …
  "UTExportedTypeDeclarations" => [
    0 => {
      "UTTypeConformsTo" => [
        0 => "com.apple.package"
        1 => "public.composite-content"
      ]
      "UTTypeDescription" => "Pages Document"
      "UTTypeIcons" => {
        "UTTypeIconText" => "PAGES"
      }
      "UTTypeIdentifier" => "com.apple.iwork.pages.pages-tef"
      "UTTypeTagSpecification" => {
        "public.filename-extension" => [
          0 => "pages-tef"
        ]
      }
    }
    …
  ]
  "UTImportedTypeDeclarations" => [
    0 => {
      "UTTypeConformsTo" => [
        0 => "public.data"
        1 => "public.composite-content"
      ]
      "UTTypeDescription" => "Microsoft Word Macro-Enabled template"
      "UTTypeIdentifier" => "org.openxmlformats.wordprocessingml.template.macro-enabled"
      "UTTypeTagSpecification" => {
        "com.apple.ostype" => [
          0 => "DOTM"
        ]
        "public.filename-extension" => [
          0 => "dotm"
        ]
      }
    }
    …
  ]
}

My Info.plist as shown by XCode looks like this:

but my application still isn't associated to the type I registered, and .dssfilelist files don't display the Icon I supplied.

Also lsregister -dump | grep dssfilelist shows multiple entries which doesn't seem correct:

amonra@Saturn ~ % lsregister -dump |grep dssfilelist
type id:                    com.github.deepskystacker-dssfilelist (0xb384)
uti:                        com.github.deepskystacker-dssfilelist
tags:                       .dssfilelist
type id:                    com.github.deepskystacker-dssfilelist (0xb390)
uti:                        com.github.deepskystacker-dssfilelist
tags:                       .dssfilelist
claimed UTIs:               com.github.deepskystacker-dssfilelist
type id:                    com.github.deepskystacker-dssfilelist (0xb39c)
uti:                        com.github.deepskystacker-dssfilelist
tags:                       .dssfilelist
bindings:                   com.github.deepskystacker-dssfilelist
bindings:                   com.github.deepskystacker-dssfilelist
type id:                    com.github.deepskystacker-dssfilelist (0xb384)
uti:                        com.github.deepskystacker-dssfilelist
tags:                       .dssfilelist
type id:                    com.github.deepskystacker-dssfilelist (0xb390)
uti:                        com.github.deepskystacker-dssfilelist
tags:                       .dssfilelist
type id:                    com.github.deepskystacker-dssfilelist (0xb39c)
uti:                        com.github.deepskystacker-dssfilelist
tags:                       .dssfilelist

do I need to add something to my Info.plist to stop that?

Hmmm here's what lsregister says about my application and the filetype it has registered (now set to com.github.deepskystacker.filelist):

---------------------------------------------------------------------------
bundle id:                  DeepSkyStacker (0x3cc)
class:                      kLSBundleClassApplication (0x2)
container:                  / (0x4)
mount state:                mounted
Mach-O UUIDs:               14010933-1443-3ABE-8993-EFF084AD3949
sequenceNum:                972
path:                       /Applications/DeepSkyStacker.app (0xa48)
directory:                  /Applications
name:                       DeepSkyStacker
localizedShortNames:        "ca" = ?, "cs" = ?, "de" = ?, "English" = ?, "es" = ?, "fr" = ?, "it" = ?, "ja_JP" = ?, "LSDefaultLocalizedValue" = "DeepSkyStacker", "nl" = ?, "pt_BR" = ?, "ro" = ?, "ru" = ?, "tr" = ?, "zh_CN" = ?, "zh_TW" = ?
teamID:                     VH8AYT3C7Y
identifier:                 com.github.deepskystacker
version:                    6.1 ({length = 32, bytes = 0x06000000 00000000 01000000 00000000 ... 00000000 00000000 })
versionString:              6.1.0
displayVersion:             6.1
mod date:                   2025-05-11 09:37 (POSIX 1746952679, 𝛥 7hrs 26min 20secs)
exec mod date:              2025-05-11 09:37 (POSIX 1746952679, 𝛥 7hrs 26min 20secs)
reg date:                   2025-05-11 16:52 (POSIX 1746978754, 𝛥 11min 45secs)
rec mod date:               2025-05-11 16:52 (POSIX 1746978754, 𝛥 11min 45secs)
type code:                  'APPL' (4150504c)
creator code:               '????' (3f3f3f3f)
plist flags:                has-custom-bindings (0000000000010000)
icon flags:                 relative-icon-path (0000000000000001)
slices:                     arm64 (0000000000000080)
item flags:                 package  application  container  native-app  extension-hidden (000000000010008e)
App Nap:                    capable (0000000000000001)
eGPU:                       capable  can-change (0000000000000005)
safeAperture system fullscreen: capable  can-change (0000000000000005)
safeAperture app fullscreen: capable  can-change (0000000000000005)
safeAperture windowed:      capable  can-change (0000000000000005)
Game Mode:                  capable  can-change (0000000000000005)
Identified Game:            capable  can-change (0000000000000005)
platform:                   native
iconDict:                   1 values (9156 (0x23c4))
                            {
                                CFBundlePrimaryIcon =     {
                                    CFBundleIconFile = "DeepSkyStacker.icns";
                                };
                            }
icons:                      Contents/Resources/DeepSkyStacker.icns
executable:                 Contents/MacOS/DeepSkyStacker
inode:                      9686765
exec inode:                 9686771
execSDK ver:                15.4 ({length = 32, bytes = 0x0f000000 00000000 04000000 00000000 ... 00000000 00000000 })
mach min ver:               13.4 ({length = 32, bytes = 0x0d000000 00000000 04000000 00000000 ... 00000000 00000000 })
infoDictionary:             15 values (9160 (0x23c8))
                            {
                                CFBundleDevelopmentRegion = English;
                                CFBundleExecutable = DeepSkyStacker;
                                CFBundleGetInfoString = "";
                                CFBundleIconFile = "DeepSkyStacker.icns";
                                CFBundleIdentifier = "com.github.deepskystacker";
                                CFBundleInfoDictionaryVersion = "6.0";
                                CFBundleLocalizations =     (
                                    ca,
                                    cs,
                                    de,
                                    es,
                                    fr,
                                    it,
                                    "ja_JP",
                                    nl,
                                    "pt_BR",
                                    ro,
                                    ru,
                                    tr,
                                    "zh_CN",
                                    "zh_TW"
                                );
                                CFBundleLongVersionString = "";
                                CFBundleName = DeepSkyStacker;
                                CFBundleNumericVersion = 101744640;
                                CFBundlePackageType = APPL;
                                CFBundleShortVersionString = "6.1";
                                CFBundleSignature = "????";
                                CFBundleVersion = "6.1.0";
                                NSHumanReadableCopyright = "Copyright \U00a9 2018-2025, David C. Partridge; Copyright \U00a9 2006-2019, Luc Coiffier";
                            }
activityTypes:              NOTIFICATION#VH8AYT3C7Y:com.github.deepskystacker, pv-eb6d5fe6c3ff51
trustedCodeSignatures:      3b627d5ab5825da80f3291707f69c8257dce381c
claimed UTIs:               com.github.deepskystacker.dssfilelist
Intents:                    0 values (9164 (0x23cc))
                            {
                            }
--------------------------------------------------------------------------------
type id:                    com.github.deepskystacker.dssfilelist (0x26c8)
bundle:                     DeepSkyStacker (0x3cc)
uti:                        com.github.deepskystacker.dssfilelist
localizedDescription:       "ca" = ?, "cs" = ?, "de" = ?, "English" = ?, "es" = ?, "fr" = ?, "it" = ?, "ja_JP" = ?, "LSDefaultLocalizedValue" = "DeepSkyStacker file-list file", "nl" = ?, "pt_BR" = ?, "ro" = ?, "ru" = ?, "tr" = ?, "zh_CN" = ?, "zh_TW" = ?
flags:                      active  exported  trusted (0000000000000051)
icons:                      1 values (9168 (0x23d0))
                            {
                                UTTypeIconFile = "DSS_filelist.icns";
                            }
conforms to:                public.plaintext
tags:                       .dssfilelist, text/dssfilelist
--------------------------------------------------------------------------------
claim id:                   3396 (0xd44)
rank:                       Owner
bundle:                     DeepSkyStacker (0x3cc)
flags:                      doc-type (0000000000000020)
roles:                      Editor (0000000000000004)
bindings:                   com.github.deepskystacker.dssfilelist

Here’s how I have this set up in one [1] of my apps:

% plcat "/Applications/Searchiverse 3.app/Contents/Info.plist"
{
  …
  "CFBundleDocumentTypes" => [
    0 => {
      "CFBundleTypeName" => "Searchiverse Index document"
      "CFBundleTypeRole" => "Editor"
      "LSItemContentTypes" => [
        0 => "com.example.apple-samplecode.searchiverse.indexdoc"
      ]
      "NSDocumentClass" => "Searchiverse.IndexDocument"
    }
    1 => {…}
  ]
  …
  "UTExportedTypeDeclarations" => [
    0 => {…}
    1 => {
      "UTTypeConformsTo" => [
        0 => "public.content"
        1 => "public.data"
      ]
      "UTTypeDescription" => "Searchiverse Index document"
      "UTTypeIconFile" => "IndexDocIcon"
      "UTTypeIdentifier" => "com.example.apple-samplecode.searchiverse.indexdoc"
      "UTTypeTagSpecification" => {
        "public.filename-extension" => [
          0 => "qcx"
        ]
      }
    }
  ]
}

Note that I must conform to public.content because that’s the base type for all document-ish things. You get this implicitly by conforming to public.plain-text.

Oh, wait, that’s something. Looking at that screenshot I see you’re using public.plaintext. You seem to be missing a dash there.

Share and Enjoy

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

[1] I have a bunch of apps that I’ve built for myself to help me get my job done more quickly.

Thanks for pointing out the the plain-text versus plaintext problem. Now why didn't the system complain when I installed the application saying "that's incorrect"...

I'm on holiday ATM, and will try this when I get back on the 19th.

Thanks again, David

I'm able to report some progress :)!

Setting that to public.plain-text made a difference, and the application is now opened when the file is double-clicked on, but it doesn't actually open the file? I expected the application to be invoked like (e.g.):

/Applications/DeepSkyStacker.app/Contents/MacOS/DeepSkyStacker Documents/Astrophotography/NGC\ 457\ Owl\ Cluster/NGC457.dssfilelist

which when typed from the command line opened the file as expected. But clearly that isn't how the application was invoked. What am I not understanding here?

The specified Icon was also not associated to the file. Even though it is in DeepSkyStacker.app/Contents/Resources

amonra@Saturn ~ % ls /Applications/DeepSkyStacker.app/Contents/Resources
DeepSkyStacker.icns	DSS_filelist.icns	qt.conf
amonra@Saturn ~ % 

I expected the application to be invoked like …

Yeah, that’s a wildly incorrect expectation )-:

When a user double clicks a document, macOS tells the app about that via an Apple event (kAEOpenDocuments). The best way to handle that depends on the GUI framework you’re using:

  • AppKit apps typically use NSDocumentController with a subclass of NSDocument. You tell the system which subclass to use by setting the NSDocumentClass property in your Info.plist.

  • In UIKit (for a Mac Catalyst app), UIDocument plays a similar role. See here.

  • In SwiftUI, you set this up using the DocumentGroup scene type. See here.

  • If you’re using a third-party GUI framework, you should consult the support resources for that framework.

Share and Enjoy

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

Thanks for that pointer. That (incorrect) expectation is based on what other platforms do, and I get that sometimes Apple does it differently!

I'm using Qt, and I now know that the relevant trick is to capture a QFileOpenEvent.

What remains is that the icon isn't displayed for the files. Any idea?

I stall can't get the icon to display :(

I change the plist to read:

			<key>UTTypeIconFile</key>
			<string>DSS_filelist.icns</string>
			<key>UTTypeIdentifier</key>
			<string>com.github.deepskystacker.dssfilelist</string>

But that didn't help. Surely someone here must know the magic incantation. WHYOHWHY doesn't the registration process flag up errors in the Info.plist file (if that's what's wrong here).

David

I get that sometimes Apple does it differently!

To be clear, there are good reasons for this difference. In macOS there’s a single app process that manages all the app’s documents. If an app is already running and the user double clicks a document, the existing app process has to open the document. So the system needs some way tell a running app to open a document. The exact mechanics of that are rooted in the deep past, but such a mechanism would have to exist even if you were doing a clean slate design.

What remains is that the icon isn't displayed for the files. Any idea?

macOS caches a lot of stuff relate to apps, file types, and icons, so I generally test this stuff on a VM. That is, I restore a VM to a snapshot that’s never seen my app, copy the app over, and see if things work there. That’s the best way to ensure that you’re not bumping into cached stuff.

Having said that, if you look at the example I posted above you’ll see that my UTTypeIconFile property doesn’t include the .icns extension.

OTOH, the icon file within the app should have the .icns extension. In my app shown above the icon is in an assert catalogue, but here’s an example from Pages that uses a standalone .icns file:

% plutil -p Pages.app/Contents/Info.plist | grep -B 6 -A 9 iBooks_Author_Document
    2 => {
      "UTTypeConformsTo" => [
        0 => "com.apple.package"
        1 => "public.composite-content"
      ]
      "UTTypeDescription" => "iBooks Author Document"
      "UTTypeIconFile" => "iBooks_Author_Document"
      "UTTypeIdentifier" => "com.apple.ibooksauthor.pkgbook"
      "UTTypeReferenceURL" => "http://www.apple.com/ibooks-author/"
      "UTTypeTagSpecification" => {
        "public.filename-extension" => [
          0 => "iba"
          1 => "book"
        ]
      }
    }
…
% file Pages.app/Contents/Resources/iBooks_Author_Document.icns  
Pages.app/Contents/Resources/iBooks_Author_Document.icns: Mac OS X icon…

Share and Enjoy

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

Hmmm....

amonra@Saturn Debug % plutil -p DeepSkyStacker.app/Contents/Info.plist
{
  "CFBundleDevelopmentRegion" => "English"
  "CFBundleDocumentTypes" => [
    0 => {
      "CFBundleTypeRole" => "Editor"
      "LSHandlerRank" => "Owner"
      "LSItemContentTypes" => [
        0 => "com.github.deepskystacker.dssfilelist"
      ]
    }
  ]
  "CFBundleExecutable" => "DeepSkyStacker"
  "CFBundleGetInfoString" => ""
  "CFBundleIconFile" => "DeepSkyStacker.icns"
  "CFBundleIdentifier" => "com.github.deepskystacker"
  "CFBundleInfoDictionaryVersion" => "6.0"
  "CFBundleLocalizations" => [
    0 => "ca"
    1 => "cs"
    2 => "de"
    3 => "es"
    4 => "fr"
    5 => "it"
    6 => "ja_JP"
    7 => "nl"
    8 => "pt_BR"
    9 => "ro"
    10 => "ru"
    11 => "tr"
    12 => "zh_CN"
    13 => "zh_TW"
  ]
  "CFBundleLongVersionString" => ""
  "CFBundleName" => "DeepSkyStacker"
  "CFBundlePackageType" => "APPL"
  "CFBundleShortVersionString" => "6.1"
  "CFBundleSignature" => "????"
  "CFBundleVersion" => "6.1.0"
  "CSResourcesFileMapped" => 1
  "NSHumanReadableCopyright" => "Copyright © 2018-2025, David C. Partridge; Copyright © 2006-2019, Luc Coiffier"
  "UTExportedTypeDeclarations" => [
    0 => {
      "UTTypeConformsTo" => [
        0 => "public.plain-text"
      ]
      "UTTypeDescription" => "DeepSkyStacker file-list file"
      "UTTypeIconFile" => "DSS_filelist"
      "UTTypeIdentifier" => "com.github.deepskystacker.dssfilelist"
      "UTTypeTagSpecification" => {
        "public.filename-extension" => [
          0 => "dssfilelist"
        ]
        "public.mime-type" => [
          0 => "text/dssfilelist"
        ]
      }
    }
  ]
}
amonra@Saturn Debug % ls DeepSkyStacker.app/Contents/Resources
DeepSkyStacker.icns	DSS_filelist.icns	qt.conf
amonra@Saturn Debug % 

looks correct to me but still the Icon refuses to display for .dssfilelist files.

As you can see, DSS_filelist.icns is in the Resources directory?

Totally baffled David

As you can see, DSS_filelist.icns is in the Resources directory?

Are you sure it’s the right format? What does file say?

Share and Enjoy

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

Associating file extension with my application
 
 
Q