First Experience With Transferable Implementing Drag And Drop In SwiftUI

First Experience With Transferable Implementing Drag And Drop In SwiftUI
⏱ Reading Time: 11 mins

In WWDC 2022 and among other interesting announcements, Apple presented a new protocol in Swift, named Transferable. In a nutshell, Transferable makes it really easy and absolutely straightforward to copy data either among different spots within the same app, or even different apps. And when talking about copy, this does not include only copy and paste, but drag and drop as well.

Before Transferable became available, it had been necessary to perform a series of certain steps in order to transfer data as mentioned above. For custom types especially, it had been mandatory to adopt specific protocols and implement some required methods in order to serialize and deserialize, work with item providers and their contained data, and more. However, all related details are not of much interest here, as the topic is the evolution of all that, the Transferable protocol. For someone that has worked with all that, Transferable is definitely a game changer.

With Transferable it takes really little to no effort to copy or drag and drop the following:

  • Objects of custom types that conform to Codable protocol
  • Data
  • Files

Note however that Transferable is not supported by any operating system before its introduction; enabling copy-paste or drag and drop in them still requires to resort to previous programming techniques. In other words, Transferable can be used in iOS 16+, macOS 13+ (Ventura and newer), watchOS 9.0+ and tvOS 16+.

This post focuses on presenting how to drag and drop data with Transferable that originate from a custom type that conforms to Codable protocol. As you will soon find out, the required lines of code related to Transferable are just a few, as it takes care of all the heavy work behind the scenes.

A demo project to work on

In order to explore what it takes in order to enable drag and drop using Transferable, we’ll focus on implementing a small project. On the one side we’ll be displaying a series of a few color item views, and on the other side there will be a small view that will be acting as a drop destination for any of these color items. Nothing fancy, but good enough to talk about Transferable.

Most importantly, each displayed color will be represented programmatically by a custom type that will be conforming to Codable protocol. That has a special weight, as we will do half of the Transferable-related work there.

Before we see all that, here’s a preview of the app we’ll be discussing here:

An iPad screen where colors from the left side of the screen are dragged to a drop view on right side.

Credits: Colors from Coolors

Preparing the app

Even though there is a link in the end of this post to download the demonstrated project, here I’m going to provide you with everything you need in order to build it fast from scratch. Once we go through that, we’ll focus on the pure discussion around Transferable.

So, in a new SwiftUI based project which you can name TransferableDemo (make sure to use Xcode 14 as the minimum required version), add a new SwiftUI view source file. You can name it ColorView.swift, as it will be displaying a color item. At the same time, keep the default view (ContentView.swift) intact.

Once it’s ready, open it and paste the following code:

This plain view implementation has a ZStack container that displays the color described by the given ColorItem object, and its name on top of it. The few view modifiers applied style the inner views a bit.

Regarding the ColorItem type, that’s something that we’ll implement in a while; as its name suggests, it’s the programming representation of a color item.

Note: If you are making this sample app from scratch following the steps here, then you’ll need to replace the ColorView_Previews struct with the following:

With the ColorView in place, let’s go to the ContentView.swift file that exists in the project by default. Add the following code there:

What this code does in short:

  • It lists a few sample colors on the left side of the screen, laying out instances of the ColorView view that we met just right above.
  • In the center there is a divider to visually separate the screen in two parts.
  • On the right side there is a VStack that will be acting as the drop area. It displays either a ColorView using the dragged color, or a prompt message if no color has been dragged yet. Notice that the border of the VStack gets its values (border color and width) from the respective state properties; we’ll be updating them dynamically while hovering a color item above this view.

The Colors type presented in the previous code snippet is also another custom type that we’ll implement right next. Consider this as the view model that provides our view with the data it needs to display.

The ColorItem type

The two views are almost ready; the only thing still missing is everything related to Transferable. So, at this point, let’s focus on the ColorItem type; the model that we made use of already, but it doesn’t exist yet.

As I’ve mentioned, ColorItem describes programmatically a color item shown on screen. Given that we eventually want to drag and drop objects of this type, it’s necessary to make sure that it conforms to Codable protocol, so let’s get started with that:

Note: You may create a new Swift source file (ColorItem.swift) in order to add the code described here.

Besides Codable, ColorItem also adopts Identifiable as well, so as to expose the id property which we use in the ForEach container in SwitUI part. In its body we’ll have the following properties:

Nothing difficult here; we have the id property as required by the Identifiable protocol, a name for the color, and three more properties that store the red, green and blue values of the color.

To help our purpose, let’s return a few sample colors from a static property, and the first round of work on the ColorItem will be considered ready:

Finally, and right before we start working on Transferable, let’s define one more custom type that we made use of already too previously; the Colors type:

When a Colors instance is initialized, the sample colors we specified in the last step are appended to the items array; that’s the datasource for the color items in the ContentView.

Defining a custom content type

When it comes to use Transferable in order to make it possible to either copy or drag and drop an object of a custom type, then there are two distinct steps that we have to make. The first is to tell the system what the type of the object we want to transfer is, or in other words, to specify the Uniform Type Identifier (UTI), also known as content type.

UTI or content type is a way invented by Apple to universally describe various types of data, such as binary files, images, text, audio, video, and a lot more, disregarding other kinds of representations, such as file extensions, MIME types, and other methods which would potentially lead to ambiguities or incompatibilities. Many content types are subtypes of other types; for instance, PNG content type is a subtype of Image type, which in turn is a subtype of Data type.

Apple provides a big number of built-in content types, however we can also define our own, custom UTIs as well. And that’s something we have to necessarily do in situations like this one, where it’s needed to let the system know what kind of data we’re planning to transfer.

The discussion about content types can become quite extensive, but doing so here would take us out of the scope of the tutorial. So, after that short overview about them, what we have to do is to declare a new content type that will be representing a ColorItem object.

To do that, click on the project name in the Project Navigator and select the TransferableDemo target (or whatever else you named the app). Then open the Info tab. Expand the Exported Type Identifiers section, click on the Plus button, and fill in the following fields with the given values:

  • Description: ColorItem
  • Identifier: This should be a unique value, and it’s recommended to prefix any value here with the Bundle Identifier of the app. For example, what I’ve set in my project is com.gabrieltheodoropoulos.TransferableDemo.color.
  • Conforms To: com.public.data

Leave the rest of the fields empty, as they are not of interest here.

Xcode screenshot showing how to fill in data regarding a custom exported UTI.

Next, head back to the file where ColorItem is implemented (or in any other source code file you prefer), and add the following extension:

Note: Make sure to set above the value that you also set in the Identifier field in the previous step.

The UTType represents a UTI programmatically, and here we are extending it in order to declare a static property matching to the custom content type defined right previously. This is not a mandatory part of the process, but it provides great convenience; we’ll be accessing just the color static property instead of writing the full UTI whenever we need to use it.

Note that it’s necessary to import the following framework so as the UTType to become available:

Making ColorItem conform to Transferable

With the first step being complete and a new custom content type defined, the next move is to make ColorItem capable of transferring instances of it. What actually happens behind the scenes is that any instance that’s about to be copied or dragged with Transferable, is serialized initially, and then it’s copied into memory. When it’s time to paste or drop it, then it’s deserialized once it’s read from memory, and afterwards it can be used just like the original instance again.

By default, custom types that conform to Codable are automatically serialized to JSON objects, but it’s also possible to serialize using other representations by providing custom encoder and decoder objects. That’s something, however, that we won’t deal with here.

Back to action again, continue by adopting the Transferable protocol to ColorItem like so:

Before going any further, make sure to import the SwiftUI framework in order to expose Transferable APIs:

Now, there is just one requirement of Transferable to satisfy; to specify at least one transferable item in the following static property:

There are specific APIs we can use here. In this particular case where we are dealing with a Codable type, so we’ll specify the CodableRepresentation as shown next:

The first argument in the CodableRepresentation initializer can be omitted, I keep it here however in order to demonstrate how the transferable type can be explicitly specified. In this case, it’s the ColorItem type.

The contentType argument though cannot be omitted; it’s required, and this is where we set the content type (UTI) of the custom type that we want to transfer. See that instead of writing the full UTI by initializing a UTType instance, we simply access the color static property we had declared in the UTType extension earlier using the dot syntax, which undeniably is really convenient and handy.

There are two more optional arguments we could also pass to the above initializer. These are the custom encoder and decoder objects, in case we want to serialize using a representation other than JSON.

The above is all we need in order to make it possible to copy-paste or drag and drop objects of the ColorItem custom type. Transferable handles everything automatically behind the scenes, without requiring any additional actions on our part.

Enabling dragging

Now that the ColorItem type successfully conforms to Transferable, it’s time to enable drag and drop, so we can move color items around in our app. There is just one place to visit for this purpose, and that is the ColorView view.

To enable dragging, it’s necessary to add one view modifier to the outermost view; the draggable(_:):

The argument we supply it with is the object that we want to drag. Needless to say the obvious, but that object must be of a type that conforms to Transferable protocol. And after our last actions, ColorItem does so.

Accepting dropping

The drop view for a color item that will be dragged around is the VStack in the ContentView. As we’ve seen in the beginning, this view displays either a color view with the dragged color item, or a prompt text message:

In order to allow a dragged color item to be dropped in that VStack, we have to use another view modifier that exists for that purpose. Here it is in its most expanded form:

The first argument that specifies the type of the dragged item is optional, and therefore it can be omitted. The isTargeted argument which is a closure is also optional, but here we have a good opportunity to talk about it. However, the second argument (first closure above) is mandatory.

It has two parameter values:

  • items is an array that contains all dragged items, but obviously when dragging one item, this array will be containing one element only.
  • location reports the position of the dragged item in the drop view, with the top-left corner being the zero point (0, 0). This value could be proved useful sometimes.

Let’s fill in the missing parts now. In the first closure, there’s just one item in the items array. We’ll assign it to the draggedColorItem state property, so it’s shown in the VStack view. However, we won’t do anything particular regarding the drop location; we’ll just print it, so we can see what the value of this location is when dragging above the drop area.

Besides implementing any custom logic in the first closure, it’s also necessary to return a Bool value from it; true if the dragged item is allowed to be dropped, false otherwise. Not all scenarios will always be that simple as we have here. Sometimes you’ll be filtering the dragged items, and occasionally you’ll need to refuse dropping.

Let’s add the related code for what I just described:

Finally, there is the last closure that is still empty. This is interesting if only you want to know whether a dragged item is inside or outside the drop area, and you want to perform any visual changes or other actions that depend on that state.

The closure’s parameter value is a Bool value, which when true, it means that the dragged item is inside the bounds of the drop area. What we’ll do here, is to update the border color and border width of the VStack like so:

A thicker border color will be shown every time a color item is dragged inside the drop area region, and will go back to normal when leaving it.

That was the last touch in this sample project that demonstrates how to use the Transferable protocol in order to perform drag and drop easily. If you’ve been following along from the beginning, you can go ahead and give the app a try, and have an actual first taste of the Transferable protocol.

Conclusion

Even though this post has become a bit long because there were quite a few things to explain, as well as to prepare the demo app, it’s a fact that the Transferable related implementation did not require much effort or coding. It’s not an exaggeration that Transferable can be a game changer as I said in the beginning in the way we copy or drag data, as it allows us to do so with the minimum possible hassle. Note that what I presented here is just a part of Transferable; there is more to explore and beyond Codable types, such as transferring data or files. More will come in future posts though. I hope you found this tutorial useful.

Thanks for reading! 🙌

You can download the sample project from this link.

If you found this post useful then please consider sharing it! Also, subscribe to my newsletter in order to be notified about everything new published here directly in your inbox, and follow me on Twitter, on YouTube, on Medium and other social media.

Stay Up To Date

Subscribe to my newsletter and get notifiied instantly when I post something new on SerialCoder.dev.

    We respect your privacy. Unsubscribe at any time.