Search code examples
iosswiftdrag-and-dropuidragitem

Drag and Drop - create an NSItemProvider from my Model


Let's go by parts!

I'm trying to implement Drag and Drop in my UICollectionViewController.

The datasource for the UICollectionView is an array of a custom Model Struct I've created.

As required I have set my collectionView.dragDelegate = self and by doing so I've implemented the required protocol function itemsForBeginning session: UIDragSession...

Here's where my problem starts:

struct Model {
    // some variables
    // Some initializations
}

var myModelDatasource: [Model] = [model1, model2, model3, ...] // it's a simple case example

func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
    let item = myModelDatasource[indexPath.row]

    let itemProvider = NSItemProvider(object: item)
    let dragItem = UIDragItem(itemProvider: itemProvider) // <-- ERROR HERE, Even If i force cast as NSItemProviderWriting
    dragItem.localObject = item

    return [dragItem]
}

I cannot create a dragItem because of my model doesn't conform to type NSItemProviderWriting.

If I force a datasource to be of type String and cast the item to NSString it works, but not with my struct Model.

Does anyone know how to resolve this issue?


Solution

  • You should use a class (not a struct) for your Model, because as you suggested you have to be conform to NSItemProviderWriting (which inherits from NSObjectProtocol):

    The protocol you implement on a class to allow an item provider to retrieve data from an instance of the class.

    Many APIs expect subclasses of NSObject, hence you have to use a class, Apple blog: struct vs class

    So your Model should be something like:

    class Model : NSObject, NSItemProviderWriting {
        public static var writableTypeIdentifiersForItemProvider: [String] {
            return [] // something here
        }
    
        public func loadData(withTypeIdentifier typeIdentifier: String, forItemProviderCompletionHandler completionHandler: @escaping (Data?, Error?) -> Swift.Void) -> Progress? {
            return nil // something here
        }
    }