I am trying to use NSFilePromiseProvider to ‘copy’ a file to the macOS pasteboard, but it always crashes. I have a macOS project that has a text file added to the bundle, and I load the file from the bundle and confirm that it exists. Then I pass the URL to a function that tries this:
let pasteboard = NSPasteboard.general
pasteboard.clearContents()
let declaredUTType = (UTType(filenameExtension: fileURL.pathExtension)?.identifier)
?? "public.data"
let promiseWriter = FilePromiseWriter(sourceURL: fileURL, fileName: fileURL.lastPathComponent)
let provider = NSFilePromiseProvider(fileType: declaredUTType, delegate: promiseWriter)
pasteboard.writeObjects([provider])
The NSFilePromiseProviderDelegate class looks this:
final class FilePromiseWriter: NSObject, NSFilePromiseProviderDelegate {
private let sourceURL: URL
private let fileName: String
init(sourceURL: URL, fileName: String) {
self.sourceURL = sourceURL
self.fileName = fileName
}
func filePromiseProvider(_ filePromiseProvider: NSFilePromiseProvider, writePromiseTo url: URL) async throws {
print("filePromiseProvider writePromiseTo url called - destinationURL: \(url)")
assert(FileManager.default.fileExists(atPath: sourceURL.path))
try FileManager.default.copyItem(at: sourceURL, to: url)
}
func filePromiseProvider(_ filePromiseProvider: NSFilePromiseProvider, fileNameForType fileType: String) -> String {
print("filePromiseProfile fileName called = \(fileName)")
return fileName
}
}
The crash comes from the pasteboard.writeObjects line. The crash comes from NSPasteboardItem.m:222
*** Assertion failure in -[NSPasteboardItem setString:forType:], NSPasteboardItem.m:222
Invalid parameter not satisfying: string
(
0 CoreFoundation 0x000000018af9aca0 __exceptionPreprocess + 176
1 libobjc.A.dylib 0x000000018aa5eb90 objc_exception_throw + 88
2 Foundation 0x000000018c5a9a78 -[NSCalendarDate initWithCoder:] + 0
3 AppKit 0x000000018f0d107c -[NSPasteboardItem setString:forType:] + 164
4 AppKit 0x000000018fa1d314 -[NSFilePromiseProvider _pasteboard:item:provideDataForType:completionHandler:] + 24
5 AppKit 0x000000018f71f958 __68-[NSPasteboard _setOwner:forTypes:atIndex:selector:usesPboardTypes:]_block_invoke + 728
6 CoreFoundation 0x000000018aff0c10 -[_CFPasteboardEntry resolvePromisedDataWithCompletionQueue:isLocalPromise:completionHandler:] + 556
7 CoreFoundation 0x000000018aff46f4 ___CFPasteboardHandleFulfillMessage_block_invoke_2 + 172
8 CoreFoundation 0x000000018af29874 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 28
....
22 TestCopyFilePasteboardCrash.debug.d 0x000000010243a308 $s27TestCopyFilePasteboardCrash0abcdE3AppV5$mainyyFZ + 40
23 TestCopyFilePasteboardCrash.debug.d 0x000000010243a3b4 __debug_main_executable_dylib_entry_point + 12
24 dyld 0x000000018aa9eb98 start + 6076
)
FAULT: NSInternalInconsistencyException: Invalid parameter not satisfying: string; {
NSAssertFile = "NSPasteboardItem.m";
NSAssertLine = 222;
}
Am I missing something, or doing something wrong here? The delete callback doesn't seem to be called at all (though I would expect it to be called when I 'paste', not when I 'copy'). I've tried with App Sandbox enabled and disabled but same results both times.
Although NSFilePromiseProvider
conforms to NSPasteboardWriting
you cannot add an instance of it to the pasteboard. NSFilePromiseProvider
is used for drag and drop operations.
To "copy" a file to the pasteboard you add its url -
guard let url = Bundle.main.url(forResource: "somefile", withExtension: "txt") as? NSURL else {
print("File not found")
return
}
let pasteboard = NSPasteboard.general
pasteboard.clearContents()
pasteboard.writeObjects([url])
After running this, if you hit ctrl-v in a Finder window you will get a copy of your file pasted into that location.