收到macOS 10.12 +

时间:2017-12-27 11:46:15

标签: cocoa swift3 swift4 nspasteboard

以前,我使用以下内容来发现来自拖放的电子邮件元数据。从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
        }

有点笨拙,但它有效,因为"com.apple.pasteboard.promised-file-url"仅在这些情况下提供。

自10.12以来,API似乎已经发生了变化,看着WWDC2016 talk Apple似乎希望我们现在使用NSFilePromiseReceiver。 我尝试了几种方法,但我无法获得承诺的文件URL。

设定:

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)
}

}

处理:

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
}

其他尝试:

    // 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? [...

任何指针都会非常感激。

2 个答案:

答案 0 :(得分:1)

我设法将文件发送到"弹出"但我无法得到他们的细节。它立即传输,然后挂起60秒,然后返回错误消息。

也许这是一个线索,但除非被注释掉并设置为true,否则checkExtension方法永远不会返回。

希望这有助于推动行动的发展:

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
    }

}

这个输出有点奇怪。 url变量aways重复我传入的目录的名称,例如

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

答案 1 :(得分:1)

我在尝试将承诺的文件接收到无效的目标网址时看到此错误。

就我而言,我在使用 Ole Begemann's Temporary File Helper 时不小心让它超出了范围,这导致在复制任何内容之前删除了目录。

receivePromisedFiles 在长时间等待后给了我 -1001 超时错误,但它仍然传递了一个根据我的输入应该正确的 URL。显然那个位置没有文件。

当我更改为有效的 url 时,一切都按预期工作。可能值得检查沙盒问题等。

Apple 现在在此处的 File Promises 部分提供了一些有用的示例项目: https://developer.apple.com/documentation/appkit/documents_data_and_pasteboard