• No se han encontrado resultados

OBLIGACIONES Y CONTRATOS, CONSTITUCIONAL Y EVIDENCIA PREGUNTA NÚM. 4

NSDictionary *metadata = [NSDictionary dictionaryWithContentsOfFile:filename];

NSString *objectIDString = [metadata valueForKey:(id)kPPObjectID];

NSURL *objectURI = [NSURL URLWithString:objectIDString];

NSPersistentStoreCoordinator *coordinator;

coordinator = [[self managedObjectContext] persistentStoreCoordinator];

NSManagedObjectID *objectID;

objectID = [coordinator managedObjectIDForURIRepresentation:objectURI];

NSManagedObject *recipe = [[self managedObjectContext] objectWithID:objectID];

if (!recipe) return NO;

dispatch_async(dispatch_get_main_queue(), ^{

NSArray *array = [NSArray arrayWithObject:recipe];

[[self recipeArrayController] setSelectedObjects:array];

});

return YES;

}

In our application delegate, we need to add the method -(BOOL)application:openFile:

that will be called when the operating system attempts to open one of our metadata files. In that method, we load the metadata file into an NSDictionary and retrieve the URIRepresentation of the NSManagedObjectID. With the NSManagedOb-jectID in hand, we can load the represented Recipe entity and display it to the user. Since we want to return from this method as quickly as possible (the operating system is waiting on an answer), we display the recipe after we return from this method.

To do that, we wrap the call to display the recipe in a dispatch_async, which updates the recipeArrayController with the selected recipe and allows the UI to update. By doing a dispatch_async and putting the execution block onto the main queue, we are effectively telling the OS to please run the block right after the current runloop completes.

With that code in place, we can select a recipe from Spotlight, and our application opens with the correct recipe selected. The first part of our OS integration is now in place.

9.2 Integrating with Quick Look

There are two different ways to implement Quick Look. The application can generate images as part of the data bundle, or a generator can be written that Chapter 9. Spotlight, Quick Look, and Core Data

168

generates the images on the fly. Storing images with the data is viable only if the data is stored in a bundle similar to the way that Pages or Numbers does. When the data is stored in a flat file, like our metadata files, a generator is the only way to integrate with Quick Look. Fortunately, writing a Quick Look generator is only slightly more complicated than a Spotlight importer.

Adding the Subproject

Just like the Spotlight importer, the Quick Look generator is created within its own subproject.

Like the Spotlight importer subproject we added earlier, we need to perform the following steps:

1. Create a subproject under our recipes project. Again, I gave mine the very clever title of QuickLookPlugin.

2. Drag the project into the main project, and flag it as a dependency.

3. Add a new copy phase to the main project’s target, and set its destination to wrapper and path to Contents/Library/QuickLook.

4. Drag the Quick Look plug-in into the new build phase.

If any of these steps are confusing, please see Xcode Subproject, on page 163.

Once the Quick Look subproject has been added, the main project’s tree should look similar to Figure 40, The Xcode project tree with all plug-ins added, on page 170.

Once the subproject has been set up properly, we will go ahead and rename the two .c files to .m files so that we can use Objective-C inside them. We need to also add Foundation.framework to the subproject so that we can utilize the Foundation APIs.

Unlike Spotlight, Quick Look has two components. There is a thumbnail generation and a preview generation. The thumbnail is used by the Finder both in place of a standard file icon and in Cover Flow. The preview is used when Quick Look is invoked in Finder, Mail, and so on. Therefore, the Quick Look template creates two .c (now .m) files, one for each. Let’s tackle the thumbnail file first.

Generating the Quick Look Thumbnail

The file GenerateThumbnailForURL.m has one function inside it that is called by the Quick Look manager (part of the operating system). This function expects we will be populating the QLThumbnailRequestRef and returning the OSStatus of noErr.

Figure 40—The Xcode project tree with all plug-ins added

Based on the documentation for Quick Look, even if we suffer a complete failure inside our plug-in, we should always return noErr.

As you can probably guess, our thumbnail generation code is going to be very simple. Since we already have an image included with each recipe, we are simply going to pass that image back whenever it is requested.

Spotlight/QuickLookPlugin/GenerateThumbnailForURL.m

OSStatus GenerateThumbnailForURL(void *thisInterface,

QLThumbnailRequestRef thumbnail, CFURLRef url,

CFStringRef contentTypeUTI, CFDictionaryRef options, CGSize maxSize)

{

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

@try {

NSDictionary *metadata;

Chapter 9. Spotlight, Quick Look, and Core Data

170

metadata = [NSDictionary dictionaryWithContentsOfURL:(NSURL*)url];

NSString *pathToImage = [metadata valueForKey:@"kPPImagePath"];

if (!pathToImage) { //No image available return noErr;

}

NSData *imageData = [NSData dataWithContentsOfFile:pathToImage];

if (!imageData) {

//Unable to load the data for some reason.

return noErr;

}

QLThumbnailRequestSetImageWithData(thumbnail, (CFDataRef)imageData, NULL);

} @finally {

[pool release], pool = nil;

}

return noErr;

}

In this method, we are again retrieving the metadata file and loading it into an NSDictionary. From that dictionary, we are retrieving the path to the image for the recipe and loading the image into an NSData object. From there, we call the QLThumbnailRequestSetImageWithData(QLThumbnailRequestRef, CFDataRect, CFDictionaryRef) method, which populates the QLThumbnailRequestRef. After that is done, we pop the NSAutoreleasePool and return noErr. From there, Quick Look uses the image we have provided whenever it needs a thumbnail for the file.

Generating the Quick Look Preview

The Quick Look preview is understandably more complex than generating a thumbnail image. If we do absolutely nothing for this part of Quick Look, we would still get a rather satisfying preview, as shown in the figure below. But why stop there when we can do so much more?

Like the thumbnail generator in Generating the Quick Look Thumbnail, on page 169, the preview generator is contained within one function call, and we are expected to populate the QLPreviewRequestRef and return noErr. Also, like the thumbnail generator, we will always return noErr no matter what happens within our function call.

Unlike the thumbnail generator, we are not going to be working with just the image for the recipe. Instead, we will generate a full HTML page that contains a large amount of information about the recipe and use that as our preview.

Although it would be possible to generate the entire HTML page in code, I am rather lazy and would rather avoid that. Instead, let’s take advantage of some XPath queries to locate the correct nodes inside a template HTML file, change the values to be appropriate for our current recipe, and use that to generate the QLPreviewRequestRef.

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

@try {

NSData *imageData = [[NSData alloc] initWithContentsOfFile:imagePath];

if (!imageData) return noErr;

To start with, we load the metadata dictionary as we have previously. We are also going to load the image data into an NSData object again. Assuming there are no issues with either the metadata or the image loading, the next step is to set up the options for the HTML page.

Spotlight/QuickLookPlugin/GeneratePreviewForURL.m

Chapter 9. Spotlight, Quick Look, and Core Data

172

[attachments setValue:imageDict forKey:@"preview-image"];

For Quick Look to be able to use the HTML page that we are handing to it, it requires that we describe the document and include any attachments it has.

This helps improve the performance of the HTML rendering, since it does not have to fetch any of the attachments. Therefore, in this section, we are setting up the properties for the HTML page, including specifying its encoding, the MIME type, and the attachments. We also give it a display name that will be used outside the HTML page.

Spotlight/QuickLookPlugin/GeneratePreviewForURL.m

NSBundle *bundle = [NSBundle bundleWithIdentifier:bundleID];

NSString *templatePath = [bundle pathForResource:@"preview" ofType:@"html"];

NSURL *templateURL = [NSURL fileURLWithPath:templatePath];

NSLog(@"Failed to build template: %@", error);

return noErr;

}

Once all the preliminaries are complete, we need to retrieve the HTML template from our bundle. Since this code is not actually being called from our bundle, we cannot just perform [NSBundle mainBundle] and get a reference to our NSBundle. (If we tried, we would actually get a reference to /usr/bin/qlmanage!) Instead, we have to request it by its UTI. With a reference to the bundle, we can then retrieve the path to preview.html, which we will be using as our template. Once we have loaded the HTML file into an NSXMLDocument, it is time to substitute the placeholders in that file with real data.

Spotlight/QuickLookPlugin/GeneratePreviewForURL.m //Updating the Title

error = nil;

NSXMLElement *element = [[template nodesForXPath:

@"/html/body/div/*[@id='title']"

error:&error] lastObject];

if (!element) {

NSLog(@"Failed to find element: %@", error);

return noErr;

NSLog(@"Failed to find element: %@", error);

return noErr;

NSLog(@"Failed to find element: %@", error);

return noErr;

NSLog(@"Failed to find element: %@", error);

return noErr;

}

NSDate *lastServedDate = [metadata valueForKey:(id)kMDItemLastUsedDate];

if (lastServedDate) {

NSDateFormatter *dateFormatter;

dateFormatter = [[[NSDateFormatter alloc] init] autorelease];

[dateFormatter setDateStyle:NSDateFormatterMediumStyle];

Chapter 9. Spotlight, Quick Look, and Core Data

174

Since we know the shape of the HTML document, we can build simple XPath queries to retrieve each part of the document and replace its value component with data from our metadata in NSDictionary.

Spotlight/QuickLookPlugin/GeneratePreviewForURL.m

QLPreviewRequestSetDataRepresentation(preview,

(CFDataRef)[template XMLData], kUTTypeHTML,

(CFDictionaryRef)properties);

} @finally {

[pool release], pool = nil;

}

return noErr;

}

Once all the data has been put into the HTML document, it is time to render it and set the QLPreviewRequestRef. As this section of code shows, we are passing in the reference along with the HTML file as data and the property NSDictionary. When this is complete, we pop the NSAutoreleasePool and return noErr. Quick Look now generates our preview and presents it to the user.

Testing the Quick Look Plug-In

At the time of this writing, testing the Quick Look plug-in is a little more challenging than testing its Spotlight counterpart. Although there is a com-mand-line option to test it, getting the system to recognize the plug-in is a bit trickier. The issue is that the system tends to ignore what generator we want it to use and will use the generator defined for the system.

In writing this chapter, I used the following workflow to test the Quick Look plug-in:

1. Clean and build the main recipe application.

2. On the command line, execute qlmanage -r to reset the Quick Look generators.

3. Run the recipe application, which causes our Quick Look generator to get registered.

4. From the command line (can also be done in Xcode), I ran qlmanage -p ${path to metadata test file}, which generated the preview. Using the -t switch instead would produce the thumbnail.

5. Rinse and repeat.

Documento similar