Drag&Drop in SwiftUI

Umut SERIFLER
5 min readJan 20, 2023

--

Moving content from one part of an app to another, or from one app to another is provided by Drag&Drop event handling.

Supports: iOS, iPadOS, Mac Catalyst 16.0+ / macOS 13+

Depending on the userflow in the applications, we sometimes might need to drag&drop content or item from one part of the app to another, or from one app to another. It is possible implementing this feature with various ways but choosing the way depends on how much time and resource we can allocate to finish it in time. Luckily, SwiftUI provides or accepts types that conforms Transferable protocol or inherits from NSItemProvider to our view modifiers. It is recommended using transferable items when possible.

Transferable

A protocol that describes how a type interacts with transport APIs such as drag&drop or copy&paste.

It is a Swift declarative way of serialising and deserialising models in app for sharing and data transfer. Conforming Transferable protocol requires implementing transferRepresentation property. transferRepresentation is used to import and export the item.

There are three important representations by default;

Data Transfer
CodableRepresentation
DataRepresentation
It is recommended using CodableRepresentation rather than DataRepresentation if a model conforms to Codable.

File Transfer
FileRepresentation
FileRepresentation is used to transfer a model involving a large amount of data such as video.

Transfer Customisation
— ProxyRepresentation

Before starting to make any change in our code, we should ask some questions to ourselves for drag&drop function. I also strongly recommend reading, or at taking a glance at, the Drag&Drop section in the Human Interface Guidelines during the implementation.

  1. What do we want to achieve with Drag&Drop?
  2. How can we implement it in an effective way and reusable?

For the first question; Even though there are many possible answers for it, finding the right answer is so important at this step because it will affect your workload and the code complexity. Depending on the objective, either we can use existing methods or define our custom methods. To keep this post simple, I’ll go with existing methods as solution.

For the second question: After we clarified our aim with Drag&Drop, it is time to think how we can make it reusable when we need somewhere else in the app.

After we answered these questions, it is time to decide:
Which type of representation should we use? or
Do we have to use any form of representation?

We don’t always have to use a representation to achieve Drag&Drop function but we have to use Transferable type.

The data we want to make draggable will take us to the answer, in other words, what kind of data needs to be transferred?

Let’s start coding to implement simple Drag&Drop function:

  1. Let’s create a list which will contain our data to move
Creates List for icons

With ForEach loop we can visualise our icons in the list like in the figure

Icon List

Now we can easily make this Image draggable by adding instance method “draggable” to activate the view as the source of a drag operation.

Draggable Function

The Draggable modifier function prototype is a defined generic type by Apple:

func draggable<T>(_ payload: @autoclosure @escaping () -> T) -> some View where T : Transferable

The “draggable” method takes a payload parameter as a single instance or a value conforming Transferable. Now it is ready to test for dragging:

Dragging Icons

Here, we need to define a property to keep our transferred data. We’ll use String type property but it might also be any other type of data such as Int, [Int], [String] etc..

// Keep moved icon
@State private var movedIcon: Image = Image(systemName: "x.circle")

It is better giving some information about how the droppable function works or which parameters we need to provide and can use from it. As it was mentioned before, the dropped content type can be provided as binary data, file URLs, or file promises but here since we drag string in our example it has to be String.self .

The DropDestination function prototype is a defined generic type by Apple:

func dropDestination<T>( for payloadType: T.Type = T.self,
action: @escaping ([T], CGPoint) -> Bool,
isTargeted: @escaping (Bool) -> Void = { _ in } ) -> some View where T : Transferable

payloadType: The expected type of the dropped models

action: A closure has two parameters: first parameter is dropped items, second parameter is the drop location of this view’s coordinate space.

isTargeted: A closure that is called when a Drag&Drop operation enters
or exits the drop target area. This feature can be used for animation or changing the UI like showing approve/cancel process icons.

If it is clear until now, let’s add the drop area to leave our data in and use a defined property to assign a new value.

Let’s add this DraggableImageArea into our code

Droppable Area with Title, Button

This code above will present us text, image and button combination where an image is designated as a type droppable. It is very important defining a type of item for dropDestion function.

Image is droppable

The drop destination is has same size and position as this movedIcon view.

Code:

Draggable Image Area
Transferable Item Code

The code is above designs the icon list and the image with clear button.

Example for dragging function

As it is shown in the sample above, after the item is selected from the left list it is ready to drag. Dropping the selected item is available only when the item is on image area.

I hope you enjoyed reading. Please leave a comment if you have any question or suggestion.

--

--

Umut SERIFLER

Professionally enjoying iOS, Android, Vapor, Nest.js development (⌚️📱🖥️🗂️)