Apple Developer Connection
Advanced Search
Member Login Log In | Not a Member? Contact ADC

Simplifying Data Handling with Uniform Type Identifiers

Uniform Type Identifiers (UTIs) are strings that specify abstract types in Mac OS X. For developers, UTIs provide a logical, hierarchical, and extensible approach to the question: "What data types does this application support?" If your Mac OS X application creates or manipulates data that may be exchanged with other applications or services, you should read this article. It provides an introduction to supporting UTIs and how your application can benefit.

With UTIs, you no longer need to specify every possible permutation of filename extensions, MIME types, and so on. Before UTIs, a graphics application might have declared its ability to open JPEG files by listing all possible JPEG types and extensions, including 'JPEG', .jpg, .jpeg, the MIME type image/jpeg, and so on. Through a mechanism known as the "conformance hierarchy", and the use of pre-declared types for common formats such as JPEG, the same application can replace those individual types with one Info.plist entry.

UTIs affect more than applications. Certain types of software depend on UTIs. For example, a Spotlight importer uses UTIs to specify the type of data the importer can handle. The pasteboard serves as the exchange mechanism between applications for data of various flavors; UTIs specify the type of each item on the pasteboard. Additional examples are provided later in this article.

UTIs hold several advantages over other type identification mechanisms:

  • The UTI naming convention is logical, and the syntax is familiar to many developers.
  • UTIs can be related in a hierarchical fashion, like a family tree. A UTI includes properties, such as a localizable user description of the type and the name of an icon resource. The property values are inherited at runtime. If a value is needed but not provided, the hierarchy is searched, starting from the bottom and going up the tree: the current type is searched first, then its supertype, and so on.
  • UTIs can be extended, meaning new types and subtypes can be created. Although you are encouraged to use Apple's declared types where possible, if a suitable UTI does not exist, you can create your own.

This article discusses each of these points, as well as providing several code examples. The reference section at the end contains a comprehensive list of resources for more information.

Note: UTIs are available in Mac OS X v10.3 Panther and later. Mac OS X v10.4 Tiger added many new types and integrated support throughout the operating system.

Integrating With Operating System Services

Supporting UTIs allows your application to take advantage of key Mac OS X technologies:

  • Launch Services: specify the document types your application can open. This can include volumes and folders as well as files.
  • Navigation Services: your application can filter the types of files displayed in open and save dialogs.
  • Pasteboard Manager: the pasteboard supports multiple data types, specified by UTIs. Your application can place items on and retrieve items from the pasteboard, using UTIs to specify the data type for each item.
  • Translation Services: translation filters convert data and files from one type to another. The source and destination types are specified using UTIs.
  • Spotlight: Metadata Importers use UTIs to specify the data types they handle.
  • Automator: actions use UTIs to specify input and output data types in the AMAccepts and AMProvides properties in the action bundle.

Base Types

A UTI is a CFString that follows a reverse Domain Name System convention. The top-level domain comes first, followed by one or more subdomains, and ending in a token that specifies the actual "type". For example, com.apple.application is an abstract base type that identifies applications. There are other abstract base types, that identify files and folders, archives, and so on, as shown in Table 1.

Table 1: Abstract Base Types Declared in UTCoreTypes.h

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 ')

Conformance Hierarchy

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.

System-Declared UTIs

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.

Declaring Supported UTIs in Info.plist

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.

Claiming a Document Type

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.

Programming with UTIs

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.

Creating a UTI

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:

  • kUTTagClassFilenameExtension
  • kUTTagClassMIMEType
  • kUTTagClassNSPboardType
  • kUTTagClassOSType

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.

Creating a UTI from a Filename Extension

Figure 1: Creating a UTI from a Filename Extension

Comparing UTIs

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 );       

Converting To and From OSTypes

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 );
}
Between an OSType and UTI

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".

Using UTIs With Pasteboards

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.

Spotlight Importer

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 );
}

For More Information

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.

Posted: 2005-8-10