7

A package is just a directory, apparently, but somehow Finder and Path Finder know it's a package.

How is this indicated in the file system? I've tried a few changes to a directory to make it a package but to no avail.

9
  • call it something.app or something.photoslibrary etc. There are many versions
    – Tetsujin
    Apr 10, 2017 at 17:23
  • @Tetsujin you're correct that it's in the filename. which I did not know. But yet DayOne app has managed to register .dayone as a package type. So... how can that be done for new pathnames? Thanks! Apr 10, 2017 at 17:27
  • 1
    Sorry, that will need someone smarter than me, I'm afraid ;)
    – Tetsujin
    Apr 10, 2017 at 17:31
  • Thanks... well if you want to add an answer to this question even given just what you've given so far, that would be great. Thanks. Apr 10, 2017 at 17:33
  • I'd wait for one of the smarter cookies - there's bound to be one along shortly :)
    – Tetsujin
    Apr 10, 2017 at 17:35

3 Answers 3

11

Short answer

To register a document as a package, the developer of DayOne would have had to modify the document type information in the DayOne app's information property list (Info.plist) file.

Long Answer

A package is a file system directory used by macOS and iOS. It's basically a directory that will appear as a single file, but which may in fact contain a preserved state of files and folders. So, a folder could have a number of subfolders and many files, but as a package it will be displayed to users like a single file. For example, in macOS the Finder will see a package as a single file to avoid users mucking around with the actual contents. However, users can view the contents through various methods such as right-clicking on the package and selecting Show Package Contents from the context menu.

Apple's Bundle Programming Guide, available on their Developer site, has a whole section on Document Packages. Below are some relevant quotes:

To register a document as a package, you must modify the document type information in your application’s information property list (Info.plist) file. The CFBundleDocumentTypes key stores information about the document types your application supports. For each document package type, include the LSTypeIsPackage key with an appropriate value. The presence of this key tells the Finder and Launch Services to treat directories with the given file extension as a package. For more information about Info.plist keys, see Information Property List Key Reference.

Document packages should always have an extension to identify them—even though that extension may be hidden by the user. The extension allows the Finder to identify your document directory and treat it as a package. You should never associate a document package with a MIME type or 4-byte OS type.

Source: Bundle Programming Guide

On the off-chance you can't access the document, let me know and I'll include some additional quotes from the page.

3
  • You code Mac Apps?
    – Pacerier
    Dec 10, 2017 at 22:04
  • No, not yet. Actually, I am slowly working on one, but it'd be my first. I've only developed iOS apps so far, or done other lower-level programming tasks (e.g. AppleScript, VBA, HTML, etc).
    – Monomeeth
    Dec 12, 2017 at 21:46
  • 1
    I'm writing Mac/iOS/tvOS applications since 1991... still, although your text is correct --- it doesn't answer the question: "Which files on my Mac WILL be presented as packages". I'm still looking for the answer. I know APIs that will tell me for a specific file, but where is the list of filename-extensions on my Mac that are registered as packages? Mar 15, 2021 at 8:14
4

As I have made a document-based application for macOS, I may help you understand a bit. Although @Monomeeth's answer is right, dealing with GUI is a lot better than Info.plist. People usually use another path. They subclass NSDocment, which provides the basis for file/bundle saving and loading. Then, people go to Xcode and under the "General" tab, define a document type, then go to "Exported UTIs" and export an UTI that conforms to com.apple.package. They read in with the NSFileWrapper class. This way, people won't have to do some tedious work with stuff like NSData.

If you want to create a bundle, check this answer. Simply create an empty macOS app and edit the "Exported UTIs" sections to taste.

2

Had the same question, and found all the answers far from satisfactory. Here's my take (after 2 weeks of research).

Most important thing to understand: MacOS (also iOS, iPadOS tvOS, watchOS) maintains some dynamic database of registered Universal Type Identifiers. Can't elaborate much here - big subject - but I'll say that a formal UTI can be paired with several "tags" (identifiers from other "spaces", like for example --- a filename Extension.

Every time a new application hits the Mac file-system, the OS peers into its Info.plist, and adds whatever new type definitions it finds there to its database.

The MacOS Framework responsible for communicating with this database is called LaunchServices and I now saw many of its APIs are deprecated and are to be replaced with the new UniformTypeIdentifiers framework - only available in MacOS Big Sur (11.x) and onward.

These frameworks are used by Finder, and all other facilities, to determine what data type to use for specific File, Directory, Package, data-in-memory, Paste-board object etc.

Some higher-level Cocoa objects (NSURL, NSFileManager, NSWorkspace and others) also wrap around these frameworks, to provide type-related functionality (e.g. to give you the URLs of applications that can open a specific data-type, or to know whether a file/directory is actually a document's package, or to have an educated guess about a file for which you do NOT have an application on your Mac).

To directly answer the OP's question - I was NOT able (even looking down at kernel level) to find a "package bit" or "bundle bit" you can set to a directory - but EVEN IF IT EXISTS, it will NOT determine OS behavior towards some directory. It is always LaunchServices being consulted on the matter.

Another important distinction is between a Bundle and a Package. A "Package" is a file-system-object (file, directory, alias, whatever) that is to be presented and manipulated AS IF it was a single object (document file). If anything relies on the mysterious "bundle bit" --- it is the "Package" aspect of a directory/file/whatever.

A "Bundle" on the other hand, is a FORMAL STRUCTURE of a directory, including an Info.plist with required entries, and other elements (such are apps, kernel-extensions, plugins etc.) that can be a package (like .app) or not (like .framework).

I don't know if OPs question comes from their development needs, but I'll provide here a few related APIs that can be helpful.

BOOL isPackage = [[NSWorkspace sharedWorkspace] isFilePackageAtPath:_filePath ]; // will only help with EXISTING path. DO READ the documentation in the header for this method.

NSURL *fileURL = [NSURL fileURLWithPath:_documentPath isDirectory:YES];
NSError *error = nil;
NSNumber *value = nil;
isPackage = ([fileURL getResourceValue:&value forKey:NSURLIsPackageKey error:&error] && [value boolValue]); // a more powerful alternative - read header for more "resource keys" you can use in this method - e.g. for determining content-type and not only the package. Again, this is meant to be used for existing files/directories.

 // this one is lower-level Core-Foundation tool that will work for non-existing objects - a "what if" question to LaunchServices.  
 CFArrayRef types = UTTypeCreateAllIdentifiersForTag( kUTTagClassFilenameExtension, (__bridge CFStringRef _Nonnull)(_filenameExtension), kUTTypePackage);
 CFIndex typeCount = CFArrayGetCount(types);
 switch (typeCount) {
     case 0: _isDocument = NO; break; // no package UTI with this filename extension is known to LaunchServices.
     case 1: {
          CFStringRef type = CFArrayGetValueAtIndex(types, 0); 
          isPackage = ! CFStringHasPrefix(type, CFSTR("dyn.")); // LaunchServices may synthesize a dynamic UTI when it doesn't know a filename extension.
          break;
     }
     default: isPackage = YES; break;
 }
 CFRelease(types);

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .