问题
Previously, I was using the following to discover e-mail meta-data from a drag & dropped e-mail(/-thread) from Mail.app.
if let filenames = draggingInfo.namesOfPromisedFilesDropped(atDestination: URL(fileURLWithPath: destinationDir!)) {
/// TODO: in future implementation Mail might return multiple filenames here.
/// So we will keep this structure to iterate the filenames
//var aPaths: [String] = []
//for _ in filenames {
if let aPath = pb.string(forType: "com.apple.pasteboard.promised-file-url") {
return aPath
}
//}
//return aPaths
}
Kind of janky, but it worked, since "com.apple.pasteboard.promised-file-url"
was only supplied in those situations.
Since 10.12 however, the API seems to have changed, and looking at the WWDC2016 talk it appears that Apple wants us to use NSFilePromiseReceiver now. I've tried a couple of approaches but I can't get a promised file URL to pop out.
Setup:
class DropzoneView: NSView {
var supportedDragTypes = [
kUTTypeURL as String, // For any URL'able types
"public.url-name", // E-mail title
"public.utf8-plain-text", // Plaintext item / E-mail thread title / calendar event date placeholder
"com.apple.pasteboard.promised-file-content-type", // Calendar event / Web URL / E-mail thread type detection
"com.apple.mail.PasteboardTypeMessageTransfer", // E-mail thread detection
"NSPromiseContentsPboardType", // E-mail thread meta-data
"com.apple.pasteboard.promised-file-url", // E-mail thread meta-data
"com.apple.NSFilePromiseItemMetaData" // E-mail thread meta-data
]
override func viewDidMoveToSuperview() {
var dragTypes = self.supportedDragTypes.map { (type) -> NSPasteboard.PasteboardType in
return NSPasteboard.PasteboardType(type)
} // Experiment:
dragTypes.append(NSPasteboard.PasteboardType.fileContentsType(forPathExtension: "eml"))
dragTypes.append(NSPasteboard.PasteboardType.fileContentsType(forPathExtension: "emlx"))
self.registerForDraggedTypes(dragTypes)
}
}
Handling:
extension DropzoneView {
override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
return .copy
}
override func draggingUpdated(_ sender: NSDraggingInfo) -> NSDragOperation {
return .copy
}
override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
let pasteboard: NSPasteboard = sender.draggingPasteboard()
guard let filePromises = pasteboard.readObjects(forClasses: [NSFilePromiseReceiver.self], options: nil) as? [NSFilePromiseReceiver] else {
return false
}
var files = [Any]()
var errors = [Error]()
let filePromiseGroup = DispatchGroup()
let operationQueue = OperationQueue()
let newTempDirectoryURL = URL(fileURLWithPath: (NSTemporaryDirectory() + (UUID().uuidString) + "/"), isDirectory: true)
do {
try FileManager.default.createDirectory(at: newTempDirectoryURL, withIntermediateDirectories: true, attributes: nil)
}
catch {
return false
}
// Async attempt, either times out after a minute or so (Error Domain=NSURLErrorDomain Code=-1001 "(null)") or gives 'operation cancelled' error
filePromises.forEach({ filePromiseReceiver in
filePromiseGroup.enter()
filePromiseReceiver.receivePromisedFiles(atDestination: newTempDirectoryURL,
options: [:],
operationQueue: operationQueue,
reader: { (url, error) in
Swift.print(url)
if let error = error {
errors.append(error)
}
else if url.isFileURL {
files.append(url)
}
else {
Swift.print("No loadable URLs found")
}
filePromiseGroup.leave()
})
})
filePromiseGroup.notify(queue: DispatchQueue.main,
execute: {
// All done, check your files and errors array
Swift.print("URLs: \(files)")
Swift.print("errors: \(errors)")
})
Swift.print("URLs: \(files)")
return true
}
Other attempts:
// returns nothing
if let filenames = pasteboard.propertyList(forType: NSPasteboard.PasteboardType(rawValue: "com.apple.pasteboard.promised-file-url")) as? NSArray {
Swift.print(filenames)
}
// doesn't result in usable URLs either
if let urls = pasteboard.readObjects(forClasses: [NSPasteboardItem.self /*NSURL.self, ???*/], options: [:]) as? [...
Any pointers would be greatly appreciated.
回答1:
I have managed to get the file to "pop out" but I cannot get the details for them. It transfers immediately and then hangs for 60 seconds before returning an error message.
Maybe it's a clue but the checkExtension method never returns unless commented out and set to true.
Hopefully this helps kick the can down the road a bit:
class DropView: NSView
{
var filePath: String?
required init?(coder: NSCoder) {
super.init(coder: coder)
self.wantsLayer = true
self.layer?.backgroundColor = NSColor.red.cgColor
registerForDraggedTypes([NSPasteboard.PasteboardType
.fileNameType(forPathExtension: ".eml"), NSPasteboard.PasteboardType.filePromise])
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
// Drawing code here.
}
override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
if checkExtension(sender) == true
{
self.layer?.backgroundColor = NSColor.blue.cgColor
return .copy
}
else
{
return NSDragOperation()
}
}
fileprivate func checkExtension(_ drag: NSDraggingInfo) -> Bool
{
return true
// guard let board = drag.draggingPasteboard().propertyList(forType: NSPasteboard.PasteboardType(rawValue: "com.apple.mail.PasteboardTypeMessageTransfer")) as? NSArray,
// let path = board[0] as? String
// else
// {
// return false
// }
//
// let suffix = URL(fileURLWithPath: path).pathExtension
// for ext in self.expectedExt
// {
// if ext.lowercased() == suffix
// {
// return true
// }
// }
// return false
}
override func draggingExited(_ sender: NSDraggingInfo?)
{
self.layer?.backgroundColor = NSColor.gray.cgColor
}
override func draggingEnded(_ sender: NSDraggingInfo)
{
self.layer?.backgroundColor = NSColor.gray.cgColor
}
override func performDragOperation(_ sender: NSDraggingInfo) -> Bool
{
let pasteboard: NSPasteboard = sender.draggingPasteboard()
guard let filePromises = pasteboard.readObjects(forClasses: [NSFilePromiseReceiver.self], options: nil) as? [NSFilePromiseReceiver] else {
return false
}
print ("Files dropped")
var files = [URL]()
let filePromiseGroup = DispatchGroup()
let operationQueue = OperationQueue()
let destURL = URL(fileURLWithPath: "/Users/andrew/Temporary", isDirectory: true)
print ("Destination URL: \(destURL)")
filePromises.forEach ({ filePromiseReceiver in
print (filePromiseReceiver)
filePromiseGroup.enter()
filePromiseReceiver.receivePromisedFiles(atDestination: destURL,
options: [:],
operationQueue: operationQueue,
reader:
{ (url, error) in
print ("Received URL: \(url)")
if let error = error
{
print ("Error: \(error)")
}
else
{
files.append(url)
}
print (filePromiseReceiver.fileNames, filePromiseReceiver.fileTypes)
filePromiseGroup.leave()
})
})
filePromiseGroup.notify(queue: DispatchQueue.main,
execute:
{
print ("Files: \(files)")
print ("Done")
})
return true
}
}
The output of this is a bit weird. The url variable aways repeats the name of the directory that I passed in eg
Files dropped
Destination URL: file:///Users/andrew/Temporary/
<NSFilePromiseReceiver: 0x6000000a1aa0>
** one minute gap **
Received URL: file:///Users/andrew/Temporary/Temporary/
Error: Error Domain=NSURLErrorDomain Code=-1001 "(null)"
["Temporary"] ["com.apple.mail.email"]
Files: []
Done
来源:https://stackoverflow.com/questions/47991603/receive-promised-e-mail-in-macos-10-12