Hi and thank you in advance for the support. I’m working on a project that allows users to drag and drop files into and out of my app using the Drag and Drop API. From my understanding, dropping a file into the app requires the file to be an NSItemProvider to be eligible for a drop - like this:
func onDrop(of: [UTType], is Targeted: Binding<Bool>?, perform: ([NSItemProvider]) -> Bool) -> some View
and dragging a file out of the app requires the same:
func onDrag (() -> NSItemProvider) -> some View
The only success I’ve had with this is actually specifying the subtype of NSItemProvider the app will be receiving via drop, or dragging away - like this:
/*Example view with drop modifier*/
ScrollView {
Text(“Dropped text”)
.onDrag {
handleDragAway(for: text)
}
}
.onDrop(of: [UTType.text.identifier], isTargeted: nil) { providers in
handleDrop(providers: providers)
}
/*Handle drop of item NSItemProvider conforming to plain text*/
func handleDropInto(providers: [NSItemProvider]) -> Bool {
var didHandle = false
for provider in providers {
if provider.hasItemConformingToTypeIdentifier(UTType.plainText.identifier) {
_ = provider.loadObject(ofClass: String.self) { string, _ in
DispatchQueue.main.async {
if let string = string {
self.handlePlainText(text: string)
didHandle = true
}
}
}
} else {
didHandle = false
}
return didHandle
}
}
/*Handle dragging the item away*/
func handleDragAway(for text: String) -> NSItemProvider {
return NSItemProvider(object: text as NSString)
}
Essentially this code, albeit incomplete, gets the idea across. Drag text into the view, display the text, drag it out. Now, all the documentation I can find says you can rinse and repeat for basically any file type (with some processing) that conforms to NSItemProvider. Here is my question: what if I don’t want to specify file type - what if I just want to tell the app, the user is dragging in some file that conforms to NSItemProvider, don’t worry about its type. Display “File” to show the user they have successfully dropped a file into the view, but don’t do any with the file (this could be implemented later). Then, the user can drag that file away, conforming to NSItemProvider, and wherever they drag it can deal with its file type. Here is an implementation of this idea:
import SwiftUI
import UniformTypeIdentifiers
struct BoardItem {
let id: UUID
let provider: NSItemProvider
}
struct BoardItemView: View {
var body: some View {
Text("File")
.frame(width: 100, height: 100)
.border(Color.black)
}
}
struct BoardView: View {
@State private var boardItems: [BoardItem] = []
var body: some View {
ScrollView {
ForEach(boardItems, id: \.id) { item in
BoardItemView()
.onDrag {
return item.provider
}
}
}
.onDrop(of: [UTType.item.identifier], isTargeted: nil) { providers in
providers.forEach { provider in
let newItem = BoardItem(id: UUID(), provider: provider)
DispatchQueue.main.async {
boardItems.append(newItem)
}
}
return true
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
The code compiles and runs easily on iPadOS or visionOS. The user can drop anything into the view, “File” appears on screen, and the user can even grab the text (that represents the file). However, dragging the object into a new app does not work. There is no green “+” element to verify the object is droppable anywhere, and attempting to drop it anywhere results in a red console message in Xcode saying
Cannot retrieve a strong reference to PBItemCollection.
Does anyone know how to fix the functionality and the error?
I appreciate you engaging in a long post, thank you!