Receive promised e-mail in macOS 10.12+

这一生的挚爱 提交于 2019-12-10 10:55:09

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!