Enabling drag-and-drop and clipboard operations with the Transferable protocol

Enabling drag-and-drop and clipboard operations with the Transferable protocol

Learn how to enable the entities of your model to work with drag-and-drop in SwiftUI.

For applications that require multiple interactions, such as dragging and dropping files or copying and pasting data, use the Transferable protocol to enable the entities of your model to be shared between applications or between areas inside the same application. To know about the Transferable protocol check the article Understanding the Transferable Protocol in Swift.

To use Copy & Paste and Drag & Drop interactions, you will need a standard structure template, as explained in detail in the article mentioned above. This structure will then be compatible with the actions we want to replicate in the application. The following example defines the type NoteItem that conforms with the Transferable protocol, providing a transfer representation for the type to be used in the context of data transferring.

import SwiftUI
import UniformTypeIdentifiers

struct NoteItem: Codable, Transferable, Identifiable {
    var id: UUID = UUID()
    var text: String

    static var transferRepresentation: some TransferRepresentation {
        DataRepresentation(contentType: .plainText) { note in
            note.text.data(using: .utf8) ?? Data()
        } importing: { data in
            let content = String(decoding: data, as: UTF8.self)
            return NoteItem(text: content)
        }
        
        ProxyRepresentation { note in
            return note.text
        }
    }
}

Copy and Paste

For copying and pasting actions use the UIPasteboard. This is an object that allows the user to share data from one location to another with the system's clipboard.

func copyToClipboard(note: NoteItem) {
    UIPasteboard.general.string = note.text
}

func pasteNote() -> NoteItem? {
    if let text = UIPasteboard.general.string {
        return NoteItem(text: text)
    }
    
    return nil
}

Using SwiftUI, it's possible to connect these functions to the interface:

  • Copy button to send content to the clipboard
  • Paste button to read from the clipboard and update the view state
struct ExampleView: View {

    @State private var notes = [
        NoteItem(text: "First note 📝"),
        NoteItem(text: "Second note ✈️")
    ]
    
    var body: some View {
        Button("Copy") {
            copyToClipboard(note: notes[index])
        }
        
        Button("Paste") {
            if let item = pasteNote() {
                droppedNotes.append(item)
            }
        }
    }
}

Drag and Drop

By conforming with Trasferable your model entities will also work with SwiftUI's Drag & Drop APIs:

Text(sourceNote.text)
    .draggable(sourceNote)

The item is already automatically packaged as .plainText because we defined that in TransferRepresentation. SwiftUI interprets the drop, converts the data, and returns your ready-to-use template:

Text(droppedText)
    .dropDestination(for: NoteItem.self) { items, _ in
        if let first = items.first {
            droppedText = "Received: \(first.text)"
        }
        return true
    }

Therefore, by adopting Transferable, you not only simplify implementation, but also make your app compatible with modern forms of interaction, from clipboard to dragging and dropping files, all with a single data model.

The following article shows in detail how to implement drag and drop using the SwiftUI drag and drop APIs. Check it out!

Implementing drag and drop with the SwiftUI modifiers
Learn how to implement drag and drop features within your SwiftUI apps.