I'm writing a Mac app with a view-based table view. It's a list of images, which I intend for the user to be able to drag to the Finder to save each image to a file.
The data source owns an array of custom model objects. The model objects all conform to the NSPasteboardWriting
protocol as follows:
- (NSArray *)writableTypesForPasteboard:(NSPasteboard *)pasteboard { //self.screenshotImageDataType is set after the image is downloaded, by examining the data with a CGImageSource. //I have verified that it is correct at this point. Its value in my test was @"public.jpeg" (kUTTypeJPEG). return @[ self.screenshotImageDataType, (__bridge NSString *)kUTTypeURL, (__bridge NSString *)kPasteboardTypeFilePromiseContent ]; } - (id)pasteboardPropertyListForType:(NSString *)type { if (UTTypeEqual((__bridge CFStringRef)type, (__bridge CFStringRef)self.screenshotImageDataType)) { return self.screenshotImageData; } else if (UTTypeEqual((__bridge CFStringRef)type, kUTTypeURL)) { return [self.screenshotImageURL pasteboardPropertyListForType:type]; } else if (UTTypeEqual((__bridge CFStringRef)type, kPasteboardTypeFilePromiseContent)) { return self.screenshotImageDataType; } id plist = [self.screenshotImage pasteboardPropertyListForType:type] ?: [self.screenshotImageURL pasteboardPropertyListForType:type]; NSLog(@"plist for type %@: %@ %p", type, [plist className], plist); return [self.screenshotImage pasteboardPropertyListForType:type] ?: [self.screenshotImageURL pasteboardPropertyListForType:type]; }
The URLs that my objects own are web URLs, not local files. They're the URLs the images were downloaded from.
The table view's data-source-and-delegate-in-one implements a method relating to file promises:
- (NSArray *)tableView:(NSTableView *)tableView namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestinationURL forDraggedRowsWithIndexes:(NSIndexSet *)rows { return [[self.screenshots objectsAtIndexes:rows] valueForKeyPath:@"screenshotImageURL.lastPathComponent"]; }
Logging the value of that expression produces a valid filename with the correct filename extension.
Finally, in windowDidLoad
, I have sent the message that turns this whole mess on:
//Enable copy drags to non-local destinations (i.e., other apps).
[self.tableView setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
The stage is set. Here's what happens when the curtains go up:
When I drag to a window owned by the Finder, the view I'm dragging to highlights, indicating that it will accept the drag.
However, when I drop the image, no file is created.
Why doesn't the file whose contents I've promised get created?