【问题标题】:Receive promised e-mail in macOS 10.12+在 macOS 10.12+ 中接收承诺的电子邮件
【发布时间】:2017-12-27 11:46:14
【问题描述】:

以前,我使用以下内容从 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? [...

任何指针将不胜感激。

【问题讨论】:

    标签: cocoa swift3 swift4 nspasteboard


    【解决方案1】:

    我已经设法让文件“弹出”,但我无法获得它们的详细信息。它立即传输,然后挂起 60 秒,然后返回错误消息。

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

    希望这有助于让事情顺利进行:

    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
    

    【讨论】:

    • 感谢@iphaaw,情节变厚了!实际上,我向 Apple 提交了支持请求并得到了退款,并显示一条消息称他们“知道这个问题,但目前没有修复或解决方法或指示何时修复它”......我从应用中撤回了我的应用存储并转移到非 osx 项目。
    • @AlexMan 您是否为此提交了雷达?你有我的链接吗?我刚刚用 Mojave 的开发者测试版进行了测试,行为仍然没有改变。
    • @GüntherEberl 现在做到了。我的被​​标记为重复。重复的错误目前仍然是Openopenradar.appspot.com/radar?id=6079749554176000
    • @AlexMan 我明白了,所以我们的问题现在在 Apple 的神秘领域。我想它仍然开放的事实可以被视为积极的。谢谢。
    【解决方案2】:

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

    在我的情况下,我使用 Ole Begemann's Temporary File Helper 并意外让它超出范围,这在复制任何内容之前删除了目录。

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

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

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

    【讨论】:

      猜你喜欢
      • 2023-04-04
      • 2016-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-27
      • 2016-07-23
      • 1970-01-01
      相关资源
      最近更新 更多