Hiding OSX file extension

I create files with proprietary extension (just a ".string") and want to hide it to the user.


I am trying the following :


        let savePanel = NSSavePanel()
        savePanel.title = "My file"
        savePanel.prompt = "Create"
        savePanel.beginWithCompletionHandler() { (result) -> Void in
            if (result == NSFileHandlingPanelOKButton) {
                let fileWithExtensionURL = savePanel.URL!.URLByAppendingPathExtension("myExt")  
                              
                saveFile()             // Create the file
                         // try to hide extension in Finder
                do { try NSFileManager.defaultManager().setAttributes([NSFileExtensionHidden: NSNumber(bool: true)], ofItemAtPath: fileWithExtensionURL.path!) } /
                catch { Swift.print("Unable to hide extension", error) }
            }   
        }   


The try is successful (no error message), but extension still shows in Finder.

Writing is done with key archiving :

    let data = NSMutableData()
    let archiver = NSKeyedArchiver(forWritingWithMutableData: data)
// encode objects
    archiver.finishEncoding()
    data.writeToURL(fileWithExtensionURL, atomically: true)


1. Is it a question of access rights to the file ? How to set it ?

Note : When I look at file information in Finder , the check box "hide extension" is set ON but disabled


2. I would also need this file to launch automatically the application if double clicked. How can I set it ?


3. I understand this should be done with UTI instead of mere file extension ; but my app is not document based, and I've found nowhere how to create UTI for the files in this case.


Thanks for any help on this.

Accepted Reply

I create files with proprietary extension (just a ".string") and want to hide it to the user.

The issue you’re having is that macOS does not know that

.string
is a file extension. You can reproduce this without any code:
  1. Create a text file called

    foo.flibble
    .
  2. In Finder, do a File > Get Info.

  3. Note the Hide Extension checkbox is greyed out.

If you repeat the steps with a file extension that the system knows by default (for example,

foo.ps
), the Hide Extension checkbox is visible.

To make this work you have to tell the system that

.string
is a valid file extension by configuring various properties in your
Info.plist
. Use
UTExportedTypeDeclarations
if your app should be considered authoritative for that extension, or
UTImportedTypeDeclarations
if you only want your app’s definitions to apply if no other definitions are available.

Finally, if you want your app to open when the user double clicks one of these files, claim it via

CFBundleDocumentTypes
.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

I have tried to set the code signing in Build Settings : no effect

I create files with proprietary extension (just a ".string") and want to hide it to the user.

The issue you’re having is that macOS does not know that

.string
is a file extension. You can reproduce this without any code:
  1. Create a text file called

    foo.flibble
    .
  2. In Finder, do a File > Get Info.

  3. Note the Hide Extension checkbox is greyed out.

If you repeat the steps with a file extension that the system knows by default (for example,

foo.ps
), the Hide Extension checkbox is visible.

To make this work you have to tell the system that

.string
is a valid file extension by configuring various properties in your
Info.plist
. Use
UTExportedTypeDeclarations
if your app should be considered authoritative for that extension, or
UTImportedTypeDeclarations
if you only want your app’s definitions to apply if no other definitions are available.

Finally, if you want your app to open when the user double clicks one of these files, claim it via

CFBundleDocumentTypes
.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks.


I’m struggling with the exact content of the info plist, to declare my filename extension (I call it XXXX, I understand it is 4 char max)

From doc, I see the required fields, but have questions on how to fill them.


UTExportedTypeDeclarations:

UTTypeConformsTo: what should be the string ?

item0 : public.data // I have tried this, doesn't seem OK

UTTypeIdentifier : com.myCompany.XXXX // Does the companyName be declared somewhere ? Is it an info somewhere in app settings ?

UTTypeDescription : docFile // I understand it is just a free label

UTTypeTagSpecification

public.filename.extension : .XXXX // Is the dot required ?



I have tried many variations for but I’m lost now !

I read in the doc:


Although a custom UTI can conform to any UTI,

public.data
or
com.apple.package
must be at the root of the conformance hierarchy for all custom UTIs that are file formats.


How do I express it in the plist ?


A complete example of custom UTI declaration for UTExportedTypeDeclarations would greatly help.

There's no need to write the UTI declaration manually, assuming you're using Xcode. For your app target, go to the project's Info editing tab, and expand the Exported UTIs section. In there, you can create your custom UTI. You'll normally just enter something in the following fields:


Description: what the Finder displays as the file type

Identifier: the UTI in com.myCompany.XXXX format

Icon: the icon image, if there is one

Conforms to: public.data [I actually use "public.content, public.data" but I don't remember why]

Extensions: the file extension


>> I understand it is 4 char max


No. In the classic Mac OS days, there was a "file type" that was exactly 4 characters, but this isn't used any more. A file extension can be as long as you want, and for a custom type longer is better, to avoid accidental conflicts with other people's extensions.

Thanks for the help.


What happens if I define Exported Type both in plist and through the project info ? Which has precedence ?


It is effectively easier to work through info tag.


The last issue I'm facing is to define CFBundleDocumentTypes so that I can double click the icone.


I define item for each type of file that can be double clicked :

- LSItemContentTypes : com.myCompany.xxxx corresponding to each UTExportedTypeDeclarations / UTTypeIdentifier

- LSHandlerrank : owner

- CFBundleTypeName : that's where I have problem ; I read in documentation : This key contains the abstract name for the document type and is used to refer to the type. What is this abstract name ??? I have tried the UTTypeIdentifier, I have tried plain text. Each time, when I double click, the app effectively opens but I get the error message :


Document could not be opened.app cannot open files with XXXX format

>> What happens if I define Exported Type both in plist and through the project info ?


There's no conflict. That entire editing page *is* info.plist. (However, it's possible that Xcode doesn't show existing UTI entries there. There might be something in the project that says you created the entry in Xcode.)


>> What is this abstract name ???


The keys are all documented here:


https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html


but this is a reference document, so related keys aren't necessarily documented in the same place.


Seriously, use the Xcode editor for document types as well as UTIs. Why do you want to do this the hard way?

If I can avoid the hard way, I'll do so.


I have to define the CFBundleDocumentTypes (as Eskimo advised).

Looking at the info, I find :

- Document Types, which effectively present the 2 types I have defined.


I wonder about the Class field : if I enter Document, the error message disappear (the file does not open, but that's probably something missing in the code).

However, my app is not docbased. So is the class Document a valid one or should it be an internal class of the app ?

Found it : implement, in AppDelegate :


func application(sender: NSApplication, openFile filename: String) -> Bool