I tried googling this, but only found results dealing with other file formats like images.
How to get the bundle identifier of the app bundle dragged into an NSTableView?
Have you gotten as far as getting an NSURL from the drop? Once you have that, create an NSBundle from it, and then can get the bundle identifier.
I solved this.
Prerequisites:
- NSTableView instance must have its DataSource object assigned:
myNSTableViewInstance.dataSource = ***
- The DataSource object conforms to NSTableViewDataSource protocol, having two necessary functions implemented (will discuss below).
- Whenever appropriate (e.g.: .windowDidLoad(), .viewDidLoad(), etc.), register the
.registerForDraggedTypes
for NSTableView instance.
In the prerequisite 3, the registration method is:
// Constructing the NSPasteboard.PasteboardType.
tblClients.registerForDraggedTypes([.init(rawValue: kUTTypeFileURL as String)]) // Compatible with macOS 10.9 Mavericks.
tblClients.registerForDraggedTypes([.fileURL]) // Since macOS 10.13 High Sierra.
In the prerequisite 2, the registration method is:
// Constructing the NSPasteboard.PasteboardType.
.init(rawValue: kUTTypeApplicationBundle as String)
We start implementing. Suppose that (in this case):
- the DataSource object is the WindowController itself.
- the name of the NSTableView instance is
tblClients
.
Since the two functions mentioned above shares a lot of things, we extract them as a standalone function with lambda expressions:
/// See whether the incoming NSDraggingInfo is an array of App Bundles.
/// - Parameters:
/// - info: the incoming NSDraggingInfo object。
/// - onError: On error perform this given lambda expression.
/// - handler: When conditions are met, perform this given lambda expression to process the URL array.
private func validatePasteboardForAppBundles(
neta info: NSDraggingInfo, onError: @escaping () -> Void?, handler: @escaping ([URL]) -> Void?
) {
let board = info.draggingPasteboard
let type = NSPasteboard.PasteboardType(rawValue: kUTTypeApplicationBundle as String)
let options: [NSPasteboard.ReadingOptionKey: Any] = [
.urlReadingFileURLsOnly: true,
.urlReadingContentsConformToTypes: [type],
]
guard let urls = board.readObjects(forClasses: [NSURL.self], options: options) as? [URL], !urls.isEmpty else {
onError()
return
}
handler(urls)
}
Finally, the two implementations.
- The one to check whether the dragged file object is the one we need to let the NSTableView response to:
func tableView(
_: NSTableView, validateDrop info: NSDraggingInfo, proposedRow _: Int,
proposedDropOperation _: NSTableView.DropOperation
) -> NSDragOperation {
var result = NSDragOperation.copy
validatePasteboardForAppBundles(
neta: info, onError: { result = .init(rawValue: 0) }, // NSDragOperationNone。
handler: { _ in }
)
return result
}
- The handling process once the mouse click releases:
func tableView(
_: NSTableView, acceptDrop info: NSDraggingInfo,
row _: Int, dropOperation _: NSTableView.DropOperation
) -> Bool {
var result = true // Outer report: Operation successful or not.
validatePasteboardForAppBundles(
neta: info, onError: { result = false } // NSDragOperationNone。
) { theURLs in
var dealt = false // Inner report. Will be true if at least one URL entry is processed with successful result.
theURLs.forEach { url in
// See whether this is an app bundle with valid bundleID:
guard let bundle = Bundle(url: url), let bundleID = bundle.bundleIdentifier else { return }
// Performing customized operations toward this bundleID string:
self.applyNewValue(bundleID, highMitigation: true)
// Mark this operation as "successful".
dealt = true
}
// Tell the outer report that whether we did at least one successful operation.
result = dealt
}
defer { if result { self.tblClients.reloadData() } }
return result
}
This should work unless there are other things screwed up.
$ EOF.