Search code examples
swiftmacosimageswiftuidrag-and-drop

DropDelegate Safari drag image


I'm trying to implement the DropDelegate pattern to allow images to be dragged into my view and load them. This works fine for images from finder, but this doesn't work when dragging images from safari into my view.

I noticed that the typeIdentifier, or UTType of the provided info/provider is not available. Can someone help me getting this image drag drop from safari to work?

struct DropImageView: View, DropDelegate {
    @State private var image = NSImage(systemSymbolName: "crop", accessibilityDescription: nil)

    func performDrop(info: DropInfo) -> Bool {
        print("Performing drop: \(info)")
        if info.hasItemsConforming(to: [.image]), let provider = info.itemProviders(for: [.image]).first {
            print(info.itemProviders(for: [String]()))
            print("Image, provider: \(provider)")
            provider.loadItem(forTypeIdentifier: "", options: nil) { something, error in
                print(something ?? error!)
            }
        } else if info.hasItemsConforming(to: [.fileURL]), let provider = info.itemProviders(for: [.fileURL]).first {
            print("FileURL, provider: \(provider)")
            provider.loadDataRepresentation(forTypeIdentifier: UTType.fileURL.identifier) { data, error in
                if let data = data, let path = String(data: data, encoding: .utf8), let url = URL(string: path) {
                    print(data, path, url)
                    DispatchQueue.main.async {
                        self.image = NSImage(contentsOf: url)
                    }
                }
            }
        } else {
            print("Unsupported, info: \(info)")
            return false
        }
        return true
    }

    var body: some View {
        VStack {
            if let image = image {
                Image(nsImage: image)
            }
            Text("Please drop your image here...")
        }
        .onDrop(of: [.image, .fileURL], delegate: self)
    }
}

Example output right now by dragging my SO profile picture

Performing drop: DropInfo(platformDropInfo: SwiftUI.(unknown context at $7fff42656708).DropInfo_Mac(location: (111.75277709960938, 28.4794921875), pasteboard: <NSPasteboard: 0x60000198e760>))
[<NSItemProvider: 0x600001681ff0> {types = (
)}]
Image, provider: <NSItemProvider: 0x600001681d50> {types = (
)}
Error Domain=NSItemProviderErrorDomain Code=-1000 "Cannot load representation of type " UserInfo={NSLocalizedDescription=Cannot load representation of type }


Solution

  • The issue was related to the type identifier that was missing. By supporting more than just the UTType.image, I did manage to get a valid drag drop working.

    func performDrop(info: DropInfo) -> Bool {
        print("Performing drop: \(info)")
        if info.hasItemsConforming(to: [.image, .jpeg, .tiff, .gif, .png, .icns, .bmp, .ico, .rawImage, .svg]) {
            let providers = info.itemProviders(for: [.image, .jpeg, .tiff, .gif, .png, .icns, .bmp, .ico, .rawImage, .svg])
    
            let types: [UTType] = [.image, .png, .jpeg, .tiff, .gif, .icns, .ico, .rawImage, .bmp, .svg]
    
            for type in types {
                for provider in providers {
                    if provider.registeredTypeIdentifiers.contains(type.identifier) {
                        print("Provider \(provider) found for \(type.identifier)")
                        provider.loadDataRepresentation(forTypeIdentifier: type.identifier) { data, error in
                            if let data = data {
                                DispatchQueue.main.async {
                                    self.image = NSImage(data: data)
                                }
                            }
                            print(data ?? error!)
                        }
                        return true
                    }
                }
            }
        }
        return false
    }