NSpasteboard writeObjects bug in Mojave

Whenever multiple objects are written to pasteboard using the writeObjects method of NSPasteboard an exception is thrown:


2018-08-25 22:41:46.006537+0200 DragTest[24732:2097487] [General] There are 2 items on the pasteboard, but 1 drag images. There must be 1 draggingItem per pasteboardItem.

2018-08-25 22:41:46.008167+0200 DragTest[24732:2097487] [General] (

0 CoreFoundation 0x00007fff386292fd __exceptionPreprocess + 256

1 libobjc.A.dylib 0x00007fff64460720 objc_exception_throw + 48

2 CoreFoundation 0x00007fff38550f5b +[NSException raise:format:] + 201

3 AppKit 0x00007fff35e06033 -[NSDraggingSession(NSInternal) _initWithPasteboard:image:offset:source:] + 247

4 AppKit 0x00007fff35e05a4b -[NSCoreDragManager dragImage:fromWindow:at:offset:event:pasteboard:source:slideBack:] + 1919

5 AppKit 0x00007fff35e052bd -[NSWindow(NSDrag) dragImage:at:offset:event:pasteboard:source:slideBack:] + 134

6 AppKit 0x00007fff361ee12f -[NSOutlineView dragImage:at:offset:event:pasteboard:source:slideBack:] + 214

7 AppKit 0x00007fff362f6491 -[NSTableView _doImageDragUsingRowsWithIndexes:event:pasteboard:source:slideBack:startRow:] + 656

8 AppKit 0x00007fff362f6925 -[NSTableView __doImageDragUsingRowsWithIndexes:event:pasteboard:source:slideBack:startRow:] + 276

9 AppKit 0x00007fff362f76ba -[NSTableView _performClassicDragOfIndexes:hitRow:event:] + 466

10 AppKit 0x00007fff35e3d890 -[NSTableView _performDragFromMouseDown:] + 474

11 AppKit 0x00007fff35e3bb95 -[NSTableView mouseDown:] + 798

12 AppKit 0x00007fff35e3b660 -[NSOutlineView mouseDown:] + 73

13 AppKit 0x00007fff35b3bb3b -[NSWindow(NSEventRouting) _handleMouseDownEvent:isDelayedEvent:] + 5668

14 AppKit 0x00007fff35b09f9f -[NSWindow(NSEventRouting) _reallySendEvent:isDelayedEvent:] + 2319

15 AppKit 0x00007fff35b094f1 -[NSWindow(NSEventRouting) sendEvent:] + 481

16 AppKit 0x00007fff35aab284 -[NSApplication(NSEvent) sendEvent:] + 336

17 AppKit 0x00007fff35bb44dd -[NSApplication run] + 755

18 AppKit 0x00007fff35b847e7 NSApplicationMain + 780

19 DragTest 0x000000010000387d main + 13

20 libdyld.dylib 0x00007fff6552d091 start + 1

)


This issue is present in the delegates of NSOutlineView, NSCollectionView and even in NSBrowser

When only one object is written it works as expected. Any idea how to avoid this?

Accepted Reply

It might be worth noting that there is a mention of this exception in the 10.14 Release Notes:


https://developer.apple.com/documentation/macos_release_notes/macos_mojave_10_14_release_notes/appkit_release_notes_for_macos_10_14?language=objc


(Look for "Drag & Drop").


However, I find what is written there quite confusing:

I am using tableView:writeRowsWithIndexes:toPasteboard, which causes this exception when dragging multiple items. THe 10.14 Release Notes make it sound like this API is deprecated. I cannot find any mention if it being deprecated though, neither in the documentation nor the header.


As according to the release notes I should be using tableView:pasteboardWriterForRow: instead, I have done this and it fixes the exception.

But - I also need to override the drag image of a multi item drag with a SINGLE image (like seen in Mail.app), and was using

dragImageForRowsWithIndexes:tableColumns:event:offset:, which is no longer called when using pasteBoardWriter. So this unforunately is no solution for me.


None of this change in behaviour (like when dragImageForRowsWithIndexes:tableColumns:event:offset: is called or not called) seems to be documented anywhere, which makes this all quite a frustrating trial and error experience.


If anybody could provide more insight into what is or is not actually deprecated, a bug or a misunderstanding of the API, this would be very helpful.


Cheers

Thomas

Replies

Did you come up with a workaround or anything? I'm hitting this also.

So, what is the answer to the question ?

looking for an answer to this error

Yeah, seeing "Correct answer" led me believing I would find the solution here… Nope.

I got a variation of the error originally reported above: "There are 0 items on the pasteboard, but 1 drag images. There must be 1 draggingItem per pasteboardItem."


I was able to get rid of that error by setting an empty string in pasteboardWriterForItem (to make it look like there was 1 draggingItem after this call):


func outlineView(_ outlineView: NSOutlineView,
                     pasteboardWriterForItem item: Any) -> NSPasteboardWriting? {

        let pasteboardItem = NSPasteboardItem()
       
        pasteboardItem.setString("", forType: NSPasteboard.PasteboardType.string)
       
        return pasteboardItem
}


Previously, I just returned an initialized NSPasteboardItem only (e.g. I added line 6).


Hopefully this, or a variation of it, will work for you.

Actually if you are using the pasteboardWriterFor.. delegates it will work fine. Unfortunately, NSBrowser has so such API (yet?) so I'm stuck.

I have filed a bugreport and it's being invetigated.

I suspect there is more than one possible cause of this error, but here's the solution I found for my own app in case it helps anyone. I determined the error was caused by using two different methods of writing data to the pasteboard. My old code looked something like this:


- (BOOL)tableView:(NSTableView *)tableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard {
NSUInteger count = rowIndexes.count;
[pboard declareTypes:@[JUNUTTypeRowIndexes,NSFilesPromisePboardType] owner:self];
NSMutableData *data = …
[pboard setData:data forType:JUNUTTypeRowIndexes];
NSMutableArray *extensions = …
[pboard setPropertyList:extensions forType:NSFilesPromisePboardType];
NSArray *urls = …
[pboard writeObjects:urls];
return YES;
}

Note that URLs are not included in declareTypes: because I was using the new writeObjects: API for those. This works fine in older versions of macOS but causes this error in Mojave. I wanted to find the simplest solution possible for a quick bug fix release, and found that using the same API for all my data types fixes the problem:


- (BOOL)tableView:(NSTableView *)tableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard {
  NSUInteger count = rowIndexes.count;
  [pboard declareTypes:@[JUNUTTypeRowIndexes,NSFilesPromisePboardType,NSURLPboardType] owner:self];
  NSMutableData *data = …
  [pboard setData:data forType:JUNUTTypeRowIndexes];
  NSMutableArray *extensions = …
  [pboard setPropertyList:extensions forType:NSFilesPromisePboardType];
  NSURL *url = …
  [url writeToPasteboard:pboard];
  return YES;
}

With this change, NSURLPboardType is now included in declareTypes: and NSURL's writeToPasteboard: method is used to write the URL to the pasteboard.


If you don't mind doing a more significant rewrite, the more modern way to handle all of this is with tableView:pasteboardWriterForRow:. This method lets you return an object that conforms to NSPasteboardWriting. That object should use writableTypesForPasteboard: to declare the content types it supports, and pasteboardPropertyListForType: to return the actual data for the requested type.

I've added

[pasteboard clearContents];


before

[pasteboard writeObjects:objects];


and currently it helped.

It might be worth noting that there is a mention of this exception in the 10.14 Release Notes:


https://developer.apple.com/documentation/macos_release_notes/macos_mojave_10_14_release_notes/appkit_release_notes_for_macos_10_14?language=objc


(Look for "Drag & Drop").


However, I find what is written there quite confusing:

I am using tableView:writeRowsWithIndexes:toPasteboard, which causes this exception when dragging multiple items. THe 10.14 Release Notes make it sound like this API is deprecated. I cannot find any mention if it being deprecated though, neither in the documentation nor the header.


As according to the release notes I should be using tableView:pasteboardWriterForRow: instead, I have done this and it fixes the exception.

But - I also need to override the drag image of a multi item drag with a SINGLE image (like seen in Mail.app), and was using

dragImageForRowsWithIndexes:tableColumns:event:offset:, which is no longer called when using pasteBoardWriter. So this unforunately is no solution for me.


None of this change in behaviour (like when dragImageForRowsWithIndexes:tableColumns:event:offset: is called or not called) seems to be documented anywhere, which makes this all quite a frustrating trial and error experience.


If anybody could provide more insight into what is or is not actually deprecated, a bug or a misunderstanding of the API, this would be very helpful.


Cheers

Thomas

Is there any update on this?

I'm happy to report that it's been fixed in the latest developer beta: 10.14.4 Beta 6 (18E220a)