In WWDC 2022 and among other interesting announcements, Apple presented a new protocol in Swift, named Transferable. In a nutshell, Transferable makes it easy and straightforward to copy data either among different spots within the same app, or even among 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 these, 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
This post focuses on how to drag and drop data of a custom type that conforms to Codable protocol using Transferable. 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.
Note
Transferable is available in iOS 16+ and macOS 13+.
A demo project to work on
In order to explore how to enable drag and drop using Transferable, we'll focus on implementing a small project. In a SwiftUI view, we'll display a series of a few color items on the one side, while on the other side there will be a small view, acting as a drop destination for these color items. Nothing fancy, but good enough to demonstrate Transferable.
Most importantly, each displayed color will be represented programmatically by a custom type, conforming to Codable protocol. That has a special weight, as the half of the Transferable-related work will be done there.
Before we see all that, here's a preview of the that app we'll go through here:

Credits: Colors from Coolors
Preparing the app
Even though there is a link in the end of this post to download the sample project, I'm going to provide you with everything you need in order to build it fast from scratch here. Once we go through that, we'll focus on the pure discussion about Transferable.
So, in a new SwiftUI based project which you can name TransferableDemo, add a new SwiftUI view source file. You can name it ColorView.swift, as it will display 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:
struct ColorView: View {
var colorItem: ColorItem
var body: some View {
ZStack(alignment: .bottomTrailing) {
Color(red: colorItem.red, green: colorItem.green, blue: colorItem.blue)
Color.black.opacity(0.3)
.frame(height: 50)
Text(colorItem.name)
.font(.title2)
.fontWeight(.bold)
.padding()
.foregroundColor(.white)
}
.cornerRadius(8)
}
}
This simple view implementation has a ZStack that contains 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:
struct ColorView_Previews: PreviewProvider {
static var previews: some View {
ColorView(colorItem: ColorItem(id: 0, name: "", red: 0, green: 0, blue: 0))
}
}
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:
struct ContentView: View {
@StateObject private var colors = Colors()
@State private var draggedColorItem: ColorItem?
@State private var borderColor: Color = .black
@State private var borderWidth: CGFloat = 1.0
var body: some View {
HStack {
VStack {
ForEach(colors.items, id: \.id) { colorItem in
ColorView(colorItem: colorItem)
}
}
.frame(width: 250)
.frame(maxHeight: 750)
.padding(.leading)
Divider().padding(.horizontal, 75)
VStack {
if draggedColorItem != nil {
ColorView(colorItem: draggedColorItem!)
} else {
Text("Drag and Drop a color here!")
.foregroundColor(.secondary)
}
}
.frame(width: 280, height: 220)
.background(Color.gray.opacity(0.25))
.border(borderColor, width: borderWidth)
.padding(.trailing)
}
}
}
Shortly, here's what this code:
- It lists a few sample colors on the left side of the screen, laying out instances of the
ColorViewview. - It uses a divider to visually separate the view in two parts.
- On the right side there is a
VStackthat will act as the drop area. It displays either aColorViewusing the dragged color, or a prompt message if no color has been dragged yet. Notice that the border of theVStackgets its values (border color and width) from the respective state properties; we'll update 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:
struct ColorItem: Identifiable, Codable {
}
Note
You can create a new Swift source file (ColorItem.swift) in order to add the code presented 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:
struct ColorItem: Identifiable, Codable {
var id: Int
var name: String
var red: Double
var green: Double
var blue: Double
}
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.
For the sake of the demonstration, let's return a few sample colors from a static property, and the first round of work on the ColorItem is considered ready:
struct ColorItem: Identifiable, Codable {
...
static var sampleColors: [ColorItem] {
[
ColorItem(id: 0, name: "Amethyst", red: 0.6, green: 0.364, blue: 0.898),
ColorItem(id: 1, name: "Magenta Crayola", red: 0.945, green: 0.357, blue: 0.71),
ColorItem(id: 2, name: "Minion Yellow", red: 0.996, green: 0.894, blue: 0.25),
ColorItem(id: 3, name: "Capri", red: 0, green: 0.734, blue: 0.976),
ColorItem(id: 4, name: "Sea Green Crayola", red: 0, green: 0.96, blue: 0.831),
]
}
}
Finally, and right before we start working with Transferable, let's define one more custom type that we made use of already too previously; the Colors type:
class Colors: ObservableObject {
@Published var items = [ColorItem]()
init() {
items.append(contentsOf: ColorItem.sampleColors)
}
}
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 enable either copy or drag and drop of a custom object, then there are two distinct steps that we have to make. The first is to tell the system the type of the object we want to transfer, or in other words, to specify the Uniform Type Identifier (UTI), also known as content type.
UTI or content type is Apple's way 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 the Image type, which in turn is a subtype of the 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 tutorial's scope. So, after that short overview about them, what we have to do is to declare a new content type that will represent a ColorItem object.
To do that, click on the project name in the Project Navigator and select the TransferableDemo target (or whatever 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.

Next, head back to the file where ColorItem is implemented, and add the following extension:
extension UTType {
static var color = UTType(exportedAs: "com.gabrieltheodoropoulos.TransferableDemo.color")
}
Important
Make sure that the above value matches to the value 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:
import UniformTypeIdentifiers
Making ColorItem conform to Transferable
With the first step completed and a new custom content type defined, the next move is to make ColorItem capable of transferring instances of it. Behind the scenes, 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 read from the memory and deserialized. After that, 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:
struct ColorItem: Identifiable, Codable, Transferable {
...
}
Before going any further, make sure to import the SwiftUI framework in order to expose the Transferable APIs:
import SwiftUI
Now, there is just one requirement of Transferable to satisfy; to specify at least one transferable item in the following static property:
struct ColorItem: Identifiable, Codable, Transferable {
static var transferRepresentation: some TransferRepresentation {
}
...
}
There are specific APIs we can use here. In this particular case we are dealing with a Codable type, so we'll specify the CodableRepresentation as shown next:
static var transferRepresentation: some TransferRepresentation {
CodableRepresentation(for: ColorItem.self, contentType: .color)
}
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 is undeniably 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 drag
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 do that, the ColorView view.
To enable dragging, it's necessary to add one view modifier to the outermost view; the draggable(_:):
ZStack(alignment: .bottomTrailing) {
...
}
// ... other view modifiers ...
.draggable(colorItem)
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 drop
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:
VStack {
if let draggedColorItem {
ColorView(colorItem: draggedColorItem)
} else {
Text("Drag and Drop a color here!")
.foregroundColor(.secondary)
}
}
// ... view modifiers ...
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:
VStack {
...
}
// ... view modifiers ...
.dropDestination(for: ColorItem.self) { items, location in
} isTargeted: { inDropArea in
}
The first argument that specifies the type of the dragged item (ColorItem.self) is optional, and therefore it can be omitted. The isTargeted argument, 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:
itemsis an array that contains all dragged items, but obviously when dragging one item, this array will contain one element only.locationreports the position of the dragged item in the drop view, with the top-left corner being the zero point (0, 0). This value can be often proved useful.
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 as simple as the one 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:
.dropDestination(for: ColorItem.self) { items, location in
draggedColorItem = items.first
print(location)
return true
} isTargeted: { inDropArea in
}
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:
.dropDestination(for: ColorItem.self) { items, location in
...
} isTargeted: { inDropArea in
print("In drop area", inDropArea)
borderColor = inDropArea ? .accentColor : .black
borderWidth = inDropArea ? 10.0 : 1.0
}
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
This post has become a bit long because there were quite a few things to explain, and to prepare the sample app. But the fact is that the Transferable related implementation did not require much effort and coding. Transferable is a game changer in the way we copy and drag data, as it allows us to do a lot 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. I hope you found this tutorial useful. Thanks for reading!