How to check whether directories with a given exte

2019-06-01 01:44发布

问题:

For a given extension, how do you check whether directories with that extension will be shown by the Finder as a package?

I figured the method below is an effective implementation for this, but creating a temporary directory feels like a hack. I'm guessing I should be able to implement this properly through the Launch Services API, but I can't quite figure out how to do it (I might be overlooking the obvious though).

// Extension method on NSWorkspace
@implementation NSWorkspace (MyExtraMethods)
- (BOOL) isPackageExtension: (NSString*) extension
{
    NSString * pathToTemp = [NSTemporaryDirectory() stringByAppendingPathComponent:[@"Untitled" stringByAppendingPathExtension: extension]];
    [[NSFileManager defaultManager] createDirectoryAtPath:pathToTemp withIntermediateDirectories:NO attributes:nil error:NULL];
    BOOL result = [[NSWorkspace sharedWorkspace] isFilePackageAtPath: pathToTemp];
    [[NSFileManager defaultManager] removeItemAtPath:pathToTemp error:NULL];
    return result;
}
@end

// Basic test for the above
- (void) testIsPackageExtension
{
    STAssertFalse([[NSWorkspace sharedWorkspace] isPackageExtension: @"txt"], @"");
    STAssertFalse([[NSWorkspace sharedWorkspace] isPackageExtension: @"rtf"], @"");

    STAssertTrue([[NSWorkspace sharedWorkspace] isPackageExtension: @"rtfd"], @"");
    STAssertTrue([[NSWorkspace sharedWorkspace] isPackageExtension: @"app"], @"");
    STAssertTrue([[NSWorkspace sharedWorkspace] isPackageExtension: @"kext"], @"");
    STAssertTrue([[NSWorkspace sharedWorkspace] isPackageExtension: @"clr"], @"");

    /* The following tests depend on having applications installed
       that are not included in Mac OS X:
        .esproj   Espresso, tested with version 2.0.5 ( http://macrabbit.com/espresso/ )
        .dtps     Instruments, included in Xcode, tested with version 4.5 (4523) */
    STAssertTrue([[NSWorkspace sharedWorkspace] isPackageExtension: @"esproj"], @"");
    STAssertTrue([[NSWorkspace sharedWorkspace] isPackageExtension: @"dtps"], @"");
}

Edit: The test above has been edited to include additional example extensions (original post only used "rtf" and "rtfd").

回答1:

You can query the Uniform Type Identifier information for your extension.
You first have to find out the correct UTI and then test if it conforms to com.apple.package (which is declared as kUTTypePackage):

- (BOOL)isPackageTypeForExtension:(NSString*)extension
{
    CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)(extension), NULL);
    return UTTypeConformsTo(UTI, kUTTypePackage);
}

Update:
Additionally you could check against kUTTypeBundle:

 UTTypeConformsTo(UTI, kUTTypeBundle)

and OR (|) the result into the return value of above method.

The following Apple docs describe how to define the UTI for a document based app (which often use NSFileWrappers & document bundles): https://developer.apple.com/library/mac/documentation/DataManagement/Conceptual/DocBasedAppProgrammingGuideForOSX/ApplicationCreationProcess/ApplicationCreationProcess.html#//apple_ref/doc/uid/TP40011179-CH6-997699

Update2:
Additionally to the UTI conforming to kUTTypePackage or kUTTypeBundle, there is a Package Bit file attribute, which can also mark a folder as package:

From the Apple's documentation:

How the System Identifies Bundles and Packages The Finder considers a directory to be a package if any of the following conditions are true:

  • The directory has a known filename extension: .app, .bundle, .framework, .plugin, .kext, and so on.
  • The directory has an extension that some other application claims represents a package type; see “Document Packages.”
  • The directory has its package bit set.

Update3:
The UTI-based approach described above, does not cover all the cases the Finder takes into account when testing if a folder is a package or bundle.
It can be used to test if a specific UTI conforms to kUTTypeBundle or kUTTypePackage though.