Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift 4: NSFilenamesPboardType not available. What to use instead for registerForDraggedTypes?

After migrating to Swift4 the following code raise compile error:

public final class MediaItemView: NSView {

   public override init(frame frameRect: NSRect) {
      super.init(frame: frameRect)

      // error: 'NSFilenamesPboardType' is unavailable in Swift:
      // use 'NSPasteboard.writeObjects(_:)' with file URLs
      let draggedTypes: [NSPasteboard.PasteboardType] = [NSFilenamesPboardType]
      registerForDraggedTypes(draggedTypes)
   }
}

What is a replacement for NSFilenamesPboardType in Swift4? How to register drag type of file name (in my case mp3, wav, aiff, ... files) in Swift4?

Thanks!

like image 912
Vlad Avatar asked Jun 14 '17 06:06

Vlad


6 Answers

I've solved backwards compatibility with this extension:

extension NSPasteboard.PasteboardType {

    static let backwardsCompatibleFileURL: NSPasteboard.PasteboardType = {

            if #available(OSX 10.13, *) {
                return NSPasteboard.PasteboardType.fileURL
            } else {
                return NSPasteboard.PasteboardType(kUTTypeFileURL as String)
            }

    } ()

}

Which means you can use NSPasteboard.PasteboardType.backwardsCompatibleFileURL

like image 78
Mark Bridges Avatar answered Oct 03 '22 06:10

Mark Bridges


i use this as the solution

    //Temp solution for this
    let NSFilenamesPboardTypeTemp = NSPasteboard.PasteboardType("NSFilenamesPboardType")

    self.zipView.registerForDraggedTypes([NSFilenamesPboardTypeTemp])

it's seem a bug from apple,them marked the api as work in 10.13 only.

i fire a bug to apple:)

like image 24
slboat Avatar answered Oct 03 '22 08:10

slboat


I like the creative workarounds presented here for the deprecated variable NSFilenamesPboardType. After looking into this question, a way to move forward with an equivalent non-deprecated approach is to use readObjects(forClasses:options:). This would also be safer WRT being able to run on future macOSes. It would be implemented like the following example, tested with Swift 4.1, based on having an NSView registered in a storyboard.

override func awakeFromNib()
{
    registerForDraggedTypes([.fileURL])
}

override func draggingEnded(_ sender: NSDraggingInfo)
{
    sender
        .draggingPasteboard()
        .readObjects(forClasses: [NSURL.self],
                     options: nil)?
        .forEach
        {
            // Do something with the file paths.
            if let url = $0 as? URL { print(url.path) }
        }
}

Since the class array parameter for readObjects is of type [AnyClass] that is the reason for the use of NSURL instead of URL.

like image 44
Daniel Zhang Avatar answered Oct 03 '22 06:10

Daniel Zhang


I'm also running into the same issue and my solution is creating a custom NSPasteboard.PasteboardType with kUTTypeURL. I'm not sure if this is the most proper way (and I suppose not), but it works at least for temporal workaround.

    let draggedType = NSPasteboard.PasteboardType(kUTTypeURL as String)
    self.tableView?.registerForDraggedTypes([draggedType])

Furthermore, the new NSPasteboard.PasteboardType has .fileNameType(forPathExtension: "foo") method. You should give a try. However somehow, it doesn't work in my case.

like image 29
1024jp Avatar answered Oct 03 '22 08:10

1024jp


Using a combination of Mark Bridges' answer and slboat's answer, this is the solution I've come up with:

extension NSPasteboard.PasteboardType {

    /// The name of a file or directory
    static let fileName: NSPasteboard.PasteboardType = {
        return NSPasteboard.PasteboardType("NSFilenamesPboardType")
    }()
}

This works as expected in my testing.

like image 38
hisaac Avatar answered Oct 03 '22 08:10

hisaac


Swift 5

//MARK:- Managing a Dragging Session After an Image Is Released

    override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
        let fetched = sender.draggingPasteboard.readObjects(forClasses:  [NSURL.self], options: nil)?
            .map({ (argv) -> String? in
                guard let url = argv as? URL else{
                    return nil
                }
                return url.path
            }).compactMap({$0})

        guard let result = fetched else{
            return false
        }
        // handle the result
        print(result)
        return true
    }
like image 1
dengST30 Avatar answered Oct 03 '22 06:10

dengST30