NSTableView to NSOutlineView Drag & Drop not accepting drop

31 views Asked by At

I have a macOS app that has been working fine for years. Full code base shows no warnings or errors. However after the latest "Recommended changes" in XCode Version 15.3 (15E204a) my compiled app does not accept item drops.

Here is a video of the behavior.

https://imgur.com/a/XgHJIXe

I thought initially it was a binding issue so I triple checked all my bindings. I then thought perhaps it was an issue with my array controller but that checked out clean. IBOutlets look fine and so I am a little lost.

Drop Object in XCode with Bindings

Drag Object in XCode with Bindings

FoodTableDragSupport.swift

import Foundation
import Cocoa

class FoodTableDragSupport: NSObject, NSTableViewDataSource, NSTableViewDelegate {
    private var mDisplayedTableView: NSTableView?
    
    var currentlyDisplayedTableView: NSTableView? {
        return mDisplayedTableView
    }
    
    func numberOfRows(in tableView: NSTableView) -> Int {
        mDisplayedTableView = tableView
        return 0 // return 0 so the table view will fall back to getting data from its binding
    }
    
    func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
        return nil // return nil so the table view will fall back to getting data from its binding
    }
    
    func tableView(_ tableView: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool {
        guard let bindingInfo = tableView.infoForBinding(.content) else {
            return false
        }
        
        guard let arrayController = bindingInfo[.observedObject] as? NSArrayController else {
            return false
        }
        
        let arrangedObjects = arrayController.arrangedObjects as! [NSManagedObject]
        
        // Collect URI representation of managed objects
        let objectURIs = rowIndexes.compactMap { row -> URL? in
            guard row < arrangedObjects.count else {
                return nil
            }
            
            let object = arrangedObjects[row]
            return object.objectID.uriRepresentation()
        }
        
        let data = try! NSKeyedArchiver.archivedData(withRootObject: objectURIs, requiringSecureCoding: false)
        pboard.declareTypes([.foodDataDragType], owner: nil)
        pboard.setData(data, forType: .foodDataDragType)
        
        return true
    }
}
 

FoodListTableDropSupport.swift

import Foundation
import Cocoa

class FoodListTableDropSupport: NSObject, NSTableViewDelegate, NSTableViewDataSource {
    @IBOutlet var ApplicationDelegate: BeFit_AppDelegate!
    @IBOutlet var myTableView: NSTableView!
    
    func numberOfRows(in tableView: NSTableView) -> Int {
        return 0 // return 0 so the table view will fall back to getting data from its binding
    }
    
    func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
        return nil // return nil so the table view will fall back to getting data from its binding
    }
    
    func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableView.DropOperation) -> NSDragOperation {
        switch dropOperation {
        case .on:
            return .copy
        default:
            return []
        }
    }
    
    func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableView.DropOperation) -> Bool {
        guard let infoForBinding = tableView.infoForBinding(.content) else {
            return false
        }
        
        guard let arrayController = infoForBinding[.observedObject] as? NSArrayController else {
            return false
        }
        
        let arrangedObjects = arrayController.arrangedObjects as! [FoodList]
        
        guard row < arrangedObjects.count else {
            return false
        }
        
        let foodListToModify = arrangedObjects[row]
        
        guard let context = ApplicationDelegate?.managedObjectContext else {
            return false
        }
        
        let data = info.draggingPasteboard.data(forType: .foodDataDragType) ?? Data()
        guard let objectURIs = (try? NSKeyedUnarchiver.unarchivedObject(ofClass: NSArray.self, from: data)) as? [URL] else {
            return false
        }
        
        for currentObjectURI in objectURIs {
            guard let objectID = context.persistentStoreCoordinator?.managedObjectID(forURIRepresentation: currentObjectURI) else {
                continue
            }
            
            guard let foodObject = context.object(with: objectID) as? Food else {
                continue
            }
            
            foodListToModify.addToFoods(foodObject)
        }
        
        myTableView.reloadData()
        return true
    }
}


Any advice, or guidance is appreciated.

#EDIT I have figured out the issue at least in theory. The line

try? NSKeyedUnarchiver.unarchivedObject(ofClass: NSArray.self, from: data)

is the part thats not working if I replace it with depreciated code it works fine.

try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data)

However trying to figure out how to do this correctly. So far I have tried following guidance from here.

I tried

if let data = info.draggingPasteboard.data(forType: .foodDataDragType) {
                if let objectURIs = (try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self], from: data)) as? [URL] {
                    for currentObjectURI in objectURIs {
                        guard let objectID = context.persistentStoreCoordinator?.managedObjectID(forURIRepresentation: currentObjectURI) else {
                            continue
                        }
                        
                        guard let foodObject = context.object(with: objectID) as? Food else {
                            continue
                        }
                        
                        foodListToModify.addToFoods(foodObject)
                    }
                }
            }
        
        
       
        myTableView.reloadData()
        return true

However while it dismissed the depreciation code it did not actually restore function to the drag drop it just continues to return false my gut is telling me that the [NSArray.self] or NSArray.self is wrong or thats the bit thats not unwrapping in the new code but no idea why.

Same issue but at least were making progress.

0

There are 0 answers