Creating a NSItemProvider for custom model class (Drag & Drop API)
For a project that I will start working on this summers, I am thinking about implementing this feature where you can drag and drop one or multiple items on a view (circleView in this tutorial) and download all those items from the web. The idea of using drag and drop is nothing new in iOS and with iOS 11, Apple has introduced a specific API for users to do just this. This API allows users to drag items within the app or from one app to another. On iPhones you can only drag the items within the app while on iPad with the help of split view you can drag items from one app to another.
The drag and drop API is extremely easy to use and configuring it takes just few minutes. There are two separate protocols that deal with drag and drop. Also there is a UITableViewDragDelegate and UITableViewDropDelegate for dragging and dropping items between table views or within same table view.
NSItemProvider is a wrapper that creates a UIDragItem. Essentially, if you want to drag and drop items of type Strings, then you first have to wrap it with an itemProvider that will convert the data to NSString and create a UIDragItem. Then when you drop the item, it will convert it from NSString to String. Apple provides ItemProvider for types like UIImage, NSURL, NSString etc
If you are populating a table view with items of custom class then in order to drag and drop these items you have to use a custom NSItemProvider. In order to do this, you need to conform your model class to NSItemProviderReading, NSItemProviderWriting.
final class PodcastModelData: NSObject, Codable, NSItemProviderReading, NSItemProviderWriting {
Make sure to use NSObject
and Codable
. Codable protocol is a combination of two other protocols - Encodable
& Decodable
. We will use Codable
to convert our object to JSON data and then when we drop it at the destination, it will revert back to the model class object.
Let’s break down the above code:
- Class properties - three variables all of type of strings
- simple initializer.
- First method to conform to - ‘writableTypeIdentifiersForItemProvider’ method returns an array of type of identifiers as Strings. Again it’s array so you can give them multiple identifiers but make sure that it’s in the order of precedence. We will be using KUTTypeData since we want to send our item as type Data.
- The second method to conform to - in this method you will convert the object to the type identifier which in our case is KUTTypeData. So we will be converting the object to JSON.
- We are simply encoding the class properties to JSON using JSONEncoder(). The progress variable keeps track of the loading / conversion.
- As soon as the loading is complete, the completion hander closure is called and the converted data is passed.
Now lets conform to NSItemProviderReading
:
Lets break down the above code:
readableTypeIdentiferForItemProvider
- again returning array of type identifiers in order of highest fidelity. In this case we are only returningKUTTypeData
.- This method creates a new instance of class with the given data and the identifier. In this method we will be using
SONDecoder()
to decode the data back to instance of class (podcastModelData). The actual function returns ‘self’ however, it was giving me some issues so I addedfinal
infront of the class name and instead of ‘self
’ wrote the class name.
That’s it really! Now you it’s pretty straight forward to create UIDragItems
with custom NSItemProvider
.
The NSitemProvider
constructor requires the object of the class and not the class itself. In this case I am giving it podcastItem. The drag item constructor requires the itemProvider which we created earlier.
Since I am dropping all these elements on a custom circleView, therefore I am using UIDropInteractionDelegate and using this method:
You can get the full source code for this project on my github here. Also check out the app in action here on my Twitter.