| Symbol | UTI | Description |
|---|---|---|
| kUTTypeItem | public.item | Generic base type for most things (files, directories) |
| kUTTypeContent | public.content | Base type for anything containing user-viewable document content (documents, pasteboard data, and document packages) |
| kUTTypeCompositeContent | public.composite-content (conforms to: public.content) | Base type for content formats supporting mixed embedded content (i.e., compound documents) |
| kUTTypeApplication | com.apple.application | Base type for Mac OS X applications, launchable items |
| kUTTypeMessage | public.message | Base type for messages (email, IM, etc.) |
| kUTTypeContact | public.contact | Contact information, e.g. for a person, group, organization |
| kUTTypeArchive | public.archive | An archive of files and directories |
| kUTTypeDiskImage | public.disk-image | A data item mountable as a volume |
Below the abstract base types in the hierarchy are concrete base types, several of which are shown in Table 2. The meaning of the column labeled "Conforms To" will be discussed in the next section.
Table 2: Several of the Concrete Base Types Declared in UTCoreTypes.h
| Symbol | UTI | Conforms To | Description |
|---|---|---|---|
| kUTTypeData | public.data | public.item | Base type for any sort of simple byte stream, including files and in-memory data |
| kUTTypeDirectory | public.directory | public.item | File system directory (includes packages AND folders) |
| kUTTypeURL | public.url | public.data | The bytes of a URL (OSType 'url ') |
If you had to specify each data type that your document can open, and all possible permutations of those types, you would probably look for a better approach. UTIs solve this dilemma by providing a hierarchy of types and subtypes. Your application specifies what type(s) it can handle, and all subtypes underneath that type are automatically included. This is the idea behind type conformance.
For example, if your application can open any type of plain text document, then you simply specify public.plain-text (also known by its symbol kUTTypePlainText) in the imported types dictionary within your application's Info.plist. Now any UTF-8, 'TEXT' (a Mac OS 9 OSType), ASCII, and other text file can be dropped on your application for opening. If your application instead only handles specialized types of text documents, you can specify one or more subtypes rather than public.plain-text, and only those subtypes will be passed to your application.
In Table 1, you may have noticed that the entry for kUTTypeCompositeContent (UTI public.composite-content) conforms to the UTI public.content. Even abstract types participate in the conformance hierarchy.
Conformance is a powerful concept. It increases flexibility while retaining a simple syntax and straightforward usage rules. In some cases, the Info.plist of your application can be modified to support additional types without requiring code changes.
Mac OS X v10.4 Tiger includes a number of pre-declared UTIs. There are many more than were shown in Tables 1 and 2. In LaunchServices.framework, which resides in ApplicationServices.framework, the header file UTCoreTypes.h includes the latest Apple-declared types. You can also find a list in the document Uniform Type Identifiers Overview. UTTypes.h (also in Launch Services) contains the functions that work on these types. Use the Apple-declared UTIs whenever possible, in preference to making up your own.
Note: In your code, use the symbolic constant for a system UTI rather than the string itself. However, in the Info.plist you will have to specify the UTI string.
Your application should identify supported UTIs in its Info.plist. This can include exported types, which your application makes available to other applications or services, and imported types, which your application accepts but does not own.
Note: You do not need to declare types provided by Apple. Only declare for export your application's unique types, and declare for import any supported third party types that are not already exported by your application.
If your application has its own data format that is unlike anything else in existence, then you may not find an appropriate system UTI to use. In this case you simply need to create your own UTI to represent each unique type, using your organization's domain name (reversed) for the first part of the UTI. Listing 1 shows how to declare an exported UTI in ACME Inc's Info.plist. This exported UTI conforms to public.plain-text. (Note that the public domain is reserved for Apple's use.) The filename extension is acme; no period is included in the property value.
Listing 1: Exporting a UTI in Info.plist
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeIdentifier</key>
<string>com.acme.acmeText</string>
<key>UTTypeDescription</key>
<string>ACME's proprietary text format</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.plain-text</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>acme</string>
</array>
</dict>
</dict>
</array>
An application that can read the com.acme.acmeText format should include the above entry in its own Info.plist. Copy-and-paste is the easiest way to accomplish this. Include the properties UTTypeIdentifier, UTTypeTagSpecification, and UTTypeConformsTo, and change UTExportedTypeDeclarations to UTImportedTypeDeclarations.
Optional properties include UTTypeIconFile, UTTypeDescription, and UTTypeReferenceURL. Be careful with UTTypeIconFile: if you do not own the icon file, you should not import it, in order to avoid any legal complications. UTTypeDescription is used by the Finder when displaying document information; the API call UTTypeCopyDescription in UTType.h allows applications to get this same information. UTTypeReferenceURL may be useful to developers scanning your (exported) Info.plist, and who need additional information about the type. These properties are described further in the Uniform Type Identifiers Overview.
You need to communicate to Launch Services the document types that your application can open. Your application "claims" these document types by including the appropriate UTIs in the application's Info.plist, and specifying a role of "Editor" or "Viewer". Listing 2 contains a fragment of an Info.plist for an application that can view PNG image files.
Listing 2: Claiming a Document Type in Info.plist
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>LSItemContentTypes</key>
<array>
<string>public.png</string>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
</array>
In the Finder, if a user drags a PNG file over this application's icon, the icon highlights to indicate that the application can open this file type. But if the user instead drags a TIFF file, the application icon will not highlight.
How does the Finder determine the appropriate behavior? Claiming of document types is performed during application installation for both Installer.app and drag installs. Any claimed types, as well as imported and exported types, are registered with Launch Services, which maintains an internal Info.plist for all installed applications as well as the operating system. At runtime, when the user drops a document onto an application icon, the Finder queries Launch Services to determine if that document can be opened by the application. The conformance hierarchy, as well as the immediate document type, may be consulted as part of this process.
UTType.h contains well-documented functions for manipulating UTIs. This section touches on only a few of the functions. We encourage you to read the documentation for more details.
The function UTTypeCreatePreferredIdentifierForTag allows you to create a UTI from a "tag". A tag is a CFString in one of several predefined formats, or "classes". Here is the declaration for UTTypeCreatePreferredIdentifierForTag:
CFStringRef UTTypeCreatePreferredIdentifierForTag( CFStringRef inTagClass, CFStringRef inTag, CFStringRef inConformingToUTI);
The first argument to UTTypeCreatePreferredIdentifierForTag specifies the tag's class, as defined in UTType.h:
Listing 3 shows how to create a UTI from a file name extension.
Listing 3: Creating a UTI
CFStringRef myUTI = UTTypeCreatePreferredIdentifierForTag( kUTTagClassFilenameExtension, CFSTR( "acme" ), kUTTypePlainText);
Figure 1 shows the value of the UTI generated by Listing 3, as captured in the debugger output. The prefix dyn in the Summary column for variable myUTI identifies this as a "dynamic type identifier", a UTI for which no declared type exists. Dynamic type identifiers carry enough information to perform equality tests against other UTIs, and to extract the original tag specification.
Figure 1: Creating a UTI from a Filename Extension
Because a UTI can carry dynamic information, you should treat it as an opaque data type. Use the following API functions to compare two UTIs, rather than performing direct CFString comparisons.
Boolean UTTypeEqual( CFStringRef inUTI1, CFStringRef inUTI2); Boolean UTTypeConformsTo( CFStringRef inUTI, CFStringRef inConformsToUTI);
The function UTTypeEqual tests for equality in two ways. First, it checks whether the CFStrings match using a case-insensitive comparison. Second, for dynamic type identifiers, this function tests whether either tag specification is a subset of the other.
UTTypeConformsTo uses the conformance hierarchy to compare types. Specific image types, such as PNG, conform to the type public.image, which should be referenced by its identifier kUTTypeImage. kUTTypeImage, in turn, conforms to kUTTypeData, which conforms to the base type kUTTypeItem (public.item). There are multiple base types at the top-level of the conformance hierarchy, so every type is not necessarily related.
In Listing 4 the first two statements return true, while the third returns false:
Listing 4: Comparing UTIs
// true : PNG is an image type Boolean result = UTTypeConformsTo( kUTTypePNG, kUTTypeImage ); // true : PNG conforms to type "data" result = UTTypeConformsTo( kUTTypePNG, kUTTypeData ); // false : PNG cannot trace through the hierarchy to type "archive" result = UTTypeConformsTo( kUTTypePNG, kUTTypeArchive );
If you use an OSType (4-byte integer) that contains non 7-bit ASCII characters, you can call UTCreateStringForOSType to create a CFString that may be passed to other UTI functions as a tag argument. Similarly, you can decode the string using UTGetOSTypeFromString.
CFStringRef UTCreateStringForOSType( OSType inOSType ); OSType UTGetOSTypeFromString( CFStringRef inString );
Listing 5 contains an example of an OSType conversion. Figure 2 shows the debugger output.
Listing 5: Converting Between an OSType and CFString
void testUTI( void )
{
CFStringRef osTypeAsString = UTCreateStringForOSType( 0xA0A0A0A0 );
OSType osType = UTGetOSTypeFromString( osTypeAsString );
}
Figure 2: Converting Between an OSType and UTI
For other OSType values, use UTTypeCreatePreferredIdentifierForTag, with a class of kUTTagClassOSType, as described previously in the section "Creating a UTI".
When an application copies data to a pasteboard, the UTI for that data gets included. In pasteboard terminology, the data item has an accompanying flavor. A receiver of the item can use the flavor to test for conformance. An example is provided in the PasteboardPeeker sample code. You can also refer to the ADC article "Carbon Pasteboards: Enhanced Data Sharing" for more details. Links to both are included at the end of this article.
For a Spotlight Metadata Importer, first specify the imported type in the Info.plist. Listing 6 shows a portion of the plist from the iff-importer sample code. A link to the sample code is provided at the end of this article. Note that the set of properties for an importer differs from the application examples presented earlier. This importer reads Amiga Interchange File Format (iff) files.
Listing 6: Partial Info.plist for a Spotlight Importer
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>MDImporter</string>
<key>LSItemContentTypes</key>
<array>
<string>com.amiga.interchange-file-format</string>
</array>
</dict>
</array>
Listing 7 shows the interface for the actual import function, again from the iff-importer example. The UTI argument is not used in this particular example, but if your importer needs to make processing decisions based on the UTI, this is a good place to do so.
Listing 7: Metadata Read Function
Boolean GetMetadataForFile( void* thisInterface,
CFMutableDictionaryRef attributes,
CFStringRef contentTypeUTI,
CFStringRef pathToFile )
{
return extract_iff_info( ( struct NSString * )pathToFile, ( struct NSMutableDictionary * )attributes );
}
Using the techniques discussed in this article, you can add UTI support to your application. The ADC Reference Library documents below will help you find more specific information.
iff-importer.LaunchServices.framework header files UTType.h, UTCoreTypes.h, and LSInfo.h. The first two files contain UTI declarations and functions for manipulating UTIs, while the last file contains additional Launch Services functions that make use of UTIs.
HIServices/TranslationServices.h contains function declarations for data and file conversion. UTIs specify the source and destination types.
Posted: 2005-8-10