Receive JSON file in DropInteraction in iOS app running in Mac Catalyst

649 views Asked by At

tl;dr: Can't load JSON file received via DropInteraction in iOS app running in Mac Catalyst.

I am currently refactoring my existing (Swift) iOS codebase to run on macOS under Mac Catalyst but am having trouble with reading, loading or even seeing JSON files that are received by my UIDropInteractionDelegate.

I am following the example here: https://appventure.me/guides/catalyst/how/drag_and_drop.html

I am trying to drop a file snse.json, which is a regular pretty-printed JSON text file, but in func 3 (performDrop), session.items is a single item array with nothing useful in it.

When I throw a breakpoint in performDrop, I get the following debug output:

(lldb) po session.items.first?.itemProvider
▿ Optional<NSItemProvider>
  - some : <NSItemProvider: 0x600003876ca0> {types = (
    "public.json",
    "com.apple.finder.node"
)}

(lldb) po session.items.first?.itemProvider.suggestedName
▿ Optional<String>
  - some : "snse.json"

(lldb) po session.items.first?.localObject
nil

(lldb) po session.items.first?.previewProvider
nil

(lldb) po session.items.first?.itemProvider.canLoadObject(ofClass: URL.self)
▿ Optional<Bool>
  - some : false

(lldb) po session.items.first?.itemProvider.canLoadObject(ofClass: String.self)
▿ Optional<Bool>
  - some : false

This is the code I have so far:

class SentimentViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.view.interactions.append(UIDropInteraction(delegate: self))
    }
}


extension SentimentViewController: UIDropInteractionDelegate {
    
    static let JSONTypeIdentifier = "public.json"
    
    // 1
    func dropInteraction(_ interaction: UIDropInteraction,
                         canHandle session: UIDropSession) -> Bool {
        return session.hasItemsConforming(toTypeIdentifiers: [JSONTypeIdentifier])
    }

    // 2
    func dropInteraction(_ interaction: UIDropInteraction, sessionDidUpdate session: UIDropSession) -> UIDropProposal {
        return UIDropProposal(operation: .copy)
    }

    // 3
    func dropInteraction(_ interaction: UIDropInteraction, performDrop session: UIDropSession) {
        // This is called with an array of NSURL
        let _ = session.loadObjects(ofClass: URL.self) { urls in
            for url in urls {
                self.importJSONData(from: url)
                print(url)
            }
        }
    }
    
    private func importJSONData(from url: URL) {
        print("I would love to load data from \(url).")
    }
}

I'm sure I'm doing something wrong, but I just can't tell what. Do I need to request permissions to read local files? Is there a step I missed? Any help is greatly appreciated!

1

There are 1 answers

1
Asperi On BEST ANSWER

You don't need URL, because drop item already has item provider for JSON data, so it is just needed to extract that data from NSItemProvider and decode.

Here is fixed parts (tested with Xcode 12.1)

func dropInteraction(_ interaction: UIDropInteraction, performDrop session: UIDropSession) {
    session.items.forEach { item in
        guard item.itemProvider.hasItemConformingToTypeIdentifier(SentimentViewController.JSONTypeIdentifier) else { return }
        item.itemProvider.loadDataRepresentation(forTypeIdentifier: SentimentViewController.JSONTypeIdentifier) { data, error in
            if let data = data {
                self.importJSONData(from: data)
            }
        }
    }
}

private func importJSONData(from data: Data) {
    print("Decode JSON from \(data.description).")
}