0

I have a UITableView that displays a Group/Note hierarchy analogous to a Finder Folder/Files hierarchy. I have implemented Drag and Drop such that if I drag a Note to the Finder Desktop an HTML file is created of the Note, and if I drag a Group to the Finder Desktop a Text file is created of the Group's title. Here is the Helper Dragging extension that I've implemented:

//
//  Model+Dragging.swift
//

/*
Abstract:
 
 Helper methods for providing and consuming drag-and-drop data.
*/

import UIKit
import MobileCoreServices

// Conditionalize Drag and Drop so it is not in iOS target.
#if targetEnvironment(macCatalyst)

extension Model {
    /**
         A helper function that serves as an interface to the data model,
         called by the implementation of the `tableView(_ canHandle:)` method.
    */
    func canHandle(_ session: UIDropSession) -> Bool {
        // In order to enable dragging all text type files changed the class
        //  loadable objects from NSString to my custom TEXTClass.
        return session.canLoadObjects(ofClass: TEXTClass.self)
    }
    
    /**
         A helper function that serves as an interface to the data model, called
         by the `tableView(_:itemsForBeginning:at:)` method.
    */
    func dragItems(for indexPath: IndexPath) -> [UIDragItem] {
        let itemProvider = NSItemProvider()
        let item = self.getDisplayItem(for: indexPath.row)
        if item is Note {
            let note:Note = item as! Note
            let html = note.noteHTML
            let data = html?.data(using: .utf8)

            // Drag to finder creates an html file.
            itemProvider.suggestedName = note.title + ".html"
            itemProvider.registerDataRepresentation(forTypeIdentifier: kUTTypeData as String, 
                                                    visibility: .all) { completion in
                completion(data, nil)
                return nil
            }
        } else {
            let group:Group = item as! Group
            let title = group.title 
            let data = title?.data(using: .utf8)

            // Drag to finder creates a text file.
            itemProvider.suggestedName = group.title + ".text"
            itemProvider.registerDataRepresentation(forTypeIdentifier: kUTTypeData as String, 
                                                    visibility: .all) { completion in
                completion(data, nil)
                return nil
            }
        }

        return [
            UIDragItem(itemProvider: itemProvider)
        ]
    }
}

#endif

I would now like to change the result of dragging a Group to the Finder. Instead of creating a Text file from the drag data, I would like to create a Folder containing the Group's Notes.

Note: I am unconcerned with the task of assembling the Group/Notes hierarchies as Folder/Files hierarchies because I already have implemented such previously as a menu export command. My concern is where and how I can communicate it to the Finder in the Drag and Drop process.

As a first step I thought I would simply create an empty folder from the drag of a Group. So far, none of my experiments have been successful. Every variation of itemProvider.registerDataRepresentation(forTypeIdentifier: or itemProvider.registerFileRepresentation(forTypeIdentifier: or registerItem(forTypeIdentifier:loadHandler: that I have tried has failed to produce anything but empty TEXT files, if they worked at all.

It is my understanding that itemProviders may provide directories instead of files, but I have been unable to find any examples of such.

My problem may be a lack of understanding of the syntax and usage of the NSItemProvider.LoadHandler completion block.

Any Swift examples on point would be greatly appreciated!

1 Answer 1

0

I was able to craft a solution with the help of this answer to another NSItemProvider stackoverflow.com question.

The solution uses a loadHandler to build the directory tree in registerDataRepresentation:

extension Model {
    …
    …
    …
/**
     A helper function that serves as an interface to the data model, called
     by the `tableView(_:itemsForBeginning:at:)` method.
*/
// Implement Menu command "ExportGroup as HTML…" as a Drag and Drop feature.//leg20240724 - TopXNotes_Catalyst_v3.0.0
func dragItems(for indexPath: IndexPath) -> [UIDragItem] {
    let itemProvider = NSItemProvider()

    
    if item is Note {
        let data = …

        // Drag to Finder creates an html file.
        itemProvider.suggestedName = …
        itemProvider.registerDataRepresentation(forTypeIdentifier: kUTTypeData as String, visibility: .all) { completion in
            completion(data, nil)
            return nil
        }
    } else {
        // Drag to Finder creates a folder tree.
        itemProvider.registerDataRepresentation(forTypeIdentifier: kUTTypeFileURL as String, visibility: .all, loadHandler: loadFileForItemProvider)
    }

    return [
        UIDragItem(itemProvider: itemProvider)
    ]
}

// Retrieve the Group tree from the tableView and assemble the
//  corresponding Directory tree for the Finder.
//
func loadFileForItemProvider(onComplete callback: @escaping (Data?, Error?) -> Void) -> Progress? {
    let progress = Progress(totalUnitCount: 100)

    
    // Build the Group directory tree in a temporary location and
    //  provide the URL in the callback.
    let groupURL =  …
    
    Task.detached {
        do {
            // Drop directory tree on the Finder.
            callback(groupURL?.dataRepresentation, nil)
                
            progress.completedUnitCount = 100
        }
    }

    return nil
}
    
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.