Mac OS X Reference Library Apple Developer
Search

Writing a Spotlight Importer

Spotlight importers should be provided by all applications that support custom document formats. A Spotlight importer parses your document format for relevant information and assigning that information to the appropriate metadata keys.

An example metadata importer that extracts metadata from a custom document is included in the /Developer/Examples/Metadata/ImporterExample. This example is referred to throughout this article.

Creating the Metadata Importer Project

Xcode provides a project template, Metadata Importer, that provides the functionality commonly shared by importers.

This template creates a project with the required frameworks, a template for the Info.plist, a template for the schema file, a template for the localizable schema.strings file, a template for the main.c file that contains the necessary CFPlugin implementation and GetMetadataForFile.c, a skeleton implementation of the required callback function. The target creates a CFPlugin bundle with an mdimporter extension.

In addition to writing the extraction code, you’ll need to modify the templates to specify the document types your importer handles and list the keys your importer provides.

Assigning a Unique ID to the Import Function

Each plug-in factory that can import metadata must have a unique identification number associated with it. Typically, there is only a single plug-in factory for each metadata importer, as a single function can handle many document types.

When you create a new metadata importer project, Xcode creates a UUID for your importer. Here is the UUID xCode generated for the sample metadata importer.

8AED83B3-C412-11D8-85A3-000393D59866

This value is used in the importer’s Info.plist, as well as the main.c file. Listing 1 shows the Info.plist template that was generated by Xcode.

Listing 1  Metadata importer Info.plist template

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
 "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleDevelopmentRegion</key>
    <string>English</string>
    <key>CFBundleDocumentTypes</key>
    <array>
        <dict>
            <key>CFBundleTypeRole</key>
            <string>MDImporter</string>
            <key>LSItemContentTypes</key>
            <array>
                <string>SUPPORTED_UTI_TYPE</string>
            </array>
        </dict>
    </array>
    <key>CFBundleExecutable</key>
    <string>MyCustomImporter</string>
    <key>CFBundleIconFile</key>
    <string></string>
    <key>CFBundleIdentifier</key>
    <string>com.apple.yourcfbundle</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundlePackageType</key>
    <string>BNDL</string>
    <key>CFBundleSignature</key>
    <string>????</string>
    <key>CFBundleVersion</key>
    <string>1.0</string>
    <key>CFPlugInDynamicRegisterFunction</key>
    <string></string>
    <key>CFPlugInDynamicRegistration</key>
    <string>NO</string>
    <key>CFPlugInFactories</key>
    <dict>
        <key>8AED83B3-C412-11D8-85A3-000393D59866</key>
        <string>MetadataImporterPluginFactory</string>
    </dict>
    <key>CFPlugInTypes</key>
    <dict>
        <key>8B08C4BF-415B-11D8-B3F9-0003936726FC</key>
        <array>
            <string>8AED83B3-C412-11D8-85A3-000393D59866</string>
        </array>
    </dict>
    <key>CFPlugInUnloadFunction</key>
    <string></string>
</dict>
</plist>

The CFPlugInFactories entry is a dictionary that associates the metadata importer host ID to the UUIDs of the plug-in factory function an importer requires. The CFPluginInTypes dictionary contains keys that associate the UUID of the factory function to the function. In both locations, Xcode inserted the newly generated UUID.

Here is the relevant line from the main.c template that Xcode created.

Listing 2  Setting the importer ID in main.c

#define PLUGIN_ID "8AED83B3-C412-11D8-85A3-000393D59866"

Associating an Importer with Document Types

An importer must be associated with the document types that it can import. You do this by specifying the Uniform Type Identifiers (UTIs) that correspond to the supported documents.

The supported UTIs are specified in the LSItemContentTypes array in the importer’s Info.plist. The template in Listing 1 includes a placeholder, SUPPORTED_UTI_TYPE, that you should replace with the UTI that your importer handles. If more than one document type is supported you can add additional string entries to the LSItemContentTypes array in the Info.plist. In the example importer, the SUPPORTED_UTI_Type is com.apple.mycocoadocumentapp.mycustomdocument.

Note: If an importer reads metadata from a document package you must add com.apple.package to the array of UTIs declared in the UTTypeConformsTo entry.

If your application does not define a UTI for its document types, you can declare one in your importer's Info.plist by adding the UTExportedTypeDeclarations key. Standalone importers that don't correspond to an application should declare the UTIs that they support by specifying a UTImportedTypeDeclarations key. The UTImportedTypeDeclarations format is the same as the UTExportedDeclarations format shown in Listing 3. See “Uniform Type Identifier Concepts” for more information on declaring UTIs.

Listing 3  UTExportedTypeDeclarations format

<key>UTExportedTypeDeclarations</key>
<array>
    <dict>
        <key>UTTypeIdentifier</key>
        <string>com.yourcompany.yourUTI</string>
        <key>UTTypeReferenceURL</key>
        <string>http://www.company.com/yourproduct</string>
        <key>UTTypeDescription</key>
        <string>Your Document Kind String</string>
        <key>UTTypeConformsTo</key>
        <array>
            <string>public.data</string>
            <string>public.content</string>
        </array>
        <key>UTTypeTagSpecification</key>
        <dict>
            <key>com.apple.ostype</key>
            <string>XXXX</string>
            <key>public.filename-extension</key>
            <array>
                <string>xxxx</string>
            </array>
        </dict>
    </dict>
</array>

Specifying Metadata Attributes

You need to specify the metadata attributes that your metadata importer returns by modifying the project’s schema.xml file. This is an XML Schema document that provides details on the returned attributes and allows you to specify custom metadata keys as well.

Listing 4 shows the schema.xml template generated by Xcode.

Listing 4  Metadata Importer schema.xml template

<?xml version="1.0" encoding="UTF-8"?>
 
<schema version="1.0" xmlns="http://www.apple.com/metadata"
                      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                      xsi:schemaLocation="http://www.apple.com/metadata
file:///System/Library/Frameworks/CoreServices.framework/Frameworks/Metadata.framework/Resources/MetadataSchema.xsd">
    <note>
        Custom attributes that this metadata importer supports.  Below
    is an example of a multivalued string attribute.  Other types
    are CFNumber, CFDate, CFBoolean and CFData.
    </note>
    <attributes>
        <attribute name="com_Foo_YourAttrName" multivalued="true" type="CFString"/>
    </attributes>
 
    <types>
        <type name="SUPPORTED_UTI_TYPE">
            <note>
        The keys that this metadata importer handles.
            </note>
            <allattrs>
        com_Foo_YourAttrName
            </allattrs>
            <displayattrs>
        com_Foo_YourAttrName
            </displayattrs>
        </type>
    </types>
</schema>

You must edit this template to suit your metadata importer.

  1. Replace the SUPPORTED_UTI_TYPE placeholder with the appropriate UTI type for your document.

  2. Edit the attributes element, editing or removing the attribute elements as required.

    The metadata keys are prefixed with the reverse DNS naming schema, replacing “_” with “.” for key-value coding compatibility. Each of these custom metadata values return a single CFString as specified by the type attribute of the attribute element.

    Metadata importers can only return the following CF types: CFString, CFNumber, CFBoolean, and CFDate. If a key returns an array of values, the type attribute specifies the CF type and the attribute element must include a multivalued attribute with a value of true.

    If your importer does not require custom metadata keys, you can remove the attributes element entirely.

  3. Edit the allattrs element so that it contains all your metadata keys.

  4. Edit the displayattrs element so that it contains a subset of your metadata keys that are recommended for previewing.

  5. Edit the schema.strings file to provide display name and description strings for your custom metadata keys.

Listing 5 shows the schema.xml file that is included with the sample metadata importer project.

Listing 5  schema.xml file for the sample metadata importer

<?xml version="1.0" encoding="UTF-8"?>
 
<schema version="1.0" xmlns="http://www.apple.com/metadata"
                      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                      xsi:schemaLocation="http://www.apple.com/metadata
file:///System/Library/Frameworks/CoreServices.framework/Frameworks/Metadata.framework/Resources/MetadataSchema.xsd">
    <note>
        Custom attributes that this metadata importer supports.  Below
    is an example of a multivalued string attribute.  Other types
    are CFNumber, CFDate, CFBoolean and CFData.
    </note>
    <attributes>
        <attribute name="com_apple_myCocoaDocumentApp_myCustomDocument_notes" multivalued="false" type="CFString"/>
    </attributes>
 
    <types>
        <type name="com.apple.mycocoadocumentapp.mycustomdocument">
            <note>
        The keys that this metadata importer handles.
            </note>
            <allattrs>
        com_apple_myCocoaDocumentApp_myCustomDocument_notes
            </allattrs>
            <displayattrs>
        com_apple_myCocoaDocumentApp_myCustomDocument_notes
            </displayattrs>
        </type>
    </types>
</schema>

The sample metadata importer declares one new attribute key, com_apple_myCocoaDocumentApp_myCustomDocument_notes. The key name is prefixed with the reverse DNS naming schema, replacing “_” with “.” for key-value coding compatibility. Each of these custom metadata values return a single CFString as specified by the type attribute of the attribute element.

Listing 6 shows the schema.strings file for the sample metadata importer.

Listing 6  Sample importer‚Äôs schema.strings file

"com_apple_myCocoaDocumentApp_myCustomDocument_notes" = "Notes";
"com_apple_myCocoaDocumentApp_myCustomDocument_notes.Description" = "What it is you're supposed to remember.";

The command-line tool mdcheckschema performs a simple validation on a schema and is useful when testing your own importer schema for validity.

Assigning Values to Metadata Attributes

When metadata is extracted for a file, the GetMetadataForFile function is called. The function is passed the plug-in interface, a mutable dictionary that you’ll add the metadata attribute keys and values to, the UTI type of the target file, and the full path to the target file.

Listing 7 shows the GetMetadataForFile skeleton implementation provided by Xcode in GetMetadataForFile.c.

Listing 7  GetMetadataForFile template implementation

Boolean GetMetadataForFile(void* thisInterface,
               CFMutableDictionaryRef attributes,
               CFStringRef contentTypeUTI,
               CFStringRef pathToFile)
{
    /* Pull any available metadata from the file at the specified path */
    /* Return the attribute keys and attribute values in the dict */
    /* Return true if successful, false if there was no data provided */
 
    #warning To complete your importer please implement the function GetMetadataForFile in GetMetadataForFile.c
    return false;
}

Your implementation of this function should extract the metadata from the file and insert it into the dictionary with the appropriate keys and values. If it successfully returns metadata, the function should return with a value of true. If no metadata was extracted, you should return false.

The example’s custom document format is a simple property list containing the author, title and reminder notes. Note that the example makes use of Objective-C and the Foundation class NSDictionary to read the dictionary from the file. In order to use Objective-C in your GetMetadataForFile implementation you must rename GetMetadataForFile.c to GetMetadataForFile.m. Listing 8 shows the GetMetadataForFile implementation of the example metadata importer.

Listing 8  Objective-C implementation of GetMetadataForFile for the sample metadata importer

Boolean GetMetadataForFile(void* thisInterface,
               CFMutableDictionaryRef attributes,
               CFStringRef contentTypeUTI,
               CFStringRef pathToFile)
{
    /* Pull any available metadata from the file at the specified path */
    /* Return the attribute keys and attribute values in the dict */
    /* Return true if successful, false if there was no data provided */
    Boolean success=NO;
    NSDictionary *tempDict;
    NSAutoreleasePool *pool;
 
    // Don't assume that there is an autorelease pool around the calling of this function.
    pool = [[NSAutoreleasePool alloc] init];
    // load the document at the specified location
    tempDict=[[NSDictionary alloc] initWithContentsOfFile:(NSString *)pathToFile];
    if (tempDict)
    {
    // set the kMDItemTitle attribute to the Title
    [(NSMutableDictionary *)attributes setObject:[tempDict objectForKey:@"title"]
                          forKey:(NSString *)kMDItemTitle];
 
    // set the kMDItemAuthors attribute to an array containing the single Author
    // value
    [(NSMutableDictionary *)attributes setObject:[NSArray arrayWithObject:[tempDict objectForKey:@"author"]]
                          forKey:(NSString *)kMDItemAuthors];
 
    // set our custom document notes attribute to the Notes value
    // (in the real world, you'd likely use the kMDItemTextContent attribute, however that
    // would make it hard to demonstrate using a custom key!)
    [(NSMutableDictionary *)attributes setObject:[tempDict objectForKey:@"notes"]
                          forKey:@"com_apple_myCocoaDocumentApp_myCustomDocument_notes"];
 
    // return YES so that the attributes are imported
    success=YES;
 
    // release the loaded document
    [tempDict release];
    }
    [pool release];
    return success;
}



Last updated: 2009-10-11

Did this document help you? Yes It's good, but... Not helpful...