Mac ऐप में दो पैन के बीच सिंथेटिक आकृतियों की प्रतिलिपि बनाने के लिए, टाइप्ड SwiftUI ड्रैग और ड्रॉप से शुरुआत करें। आइटम प्रोवाइडर तब चुनें, जब मौजूदा प्रोवाइडर इंटरफ़ेस या macOS का पुराना लक्ष्य उनकी माँग करे। किसी भी API को चुनने से पहले पेलोड, स्वीकृति नियम और मॉडल के स्वामी तय करें।

तय करें कि सफल ड्रॉप का अर्थ क्या है

यहाँ पाठक का काम स्पष्ट है: किसी को पैलेट से सिंथेटिक वृत्त या वर्ग को उसी Mac ऐप में मौजूद संग्रह में खींचने दें। गंतव्य एक नया आइटम बनाता है; पैलेट अपरिवर्तित रहता है। सब कुछ मेमोरी में रहता है और विंडो बंद करने पर संग्रह मिट जाता है।

Apple, Swift ड्रैग और ड्रॉप के लिए transferable आइटम सुझाता है। इसके दस्तावेज़ provider-आधारित मॉडिफ़ायर का भी समर्थन करते हैं, इसलिए चुनाव परिनियोजन आवश्यकताओं और आपके लिए आवश्यक एकीकरण इंटरफ़ेस पर निर्भर करता है। ड्रैग और ड्रॉप — Apple।

व्यू लागू करने से पहले ट्रांसफ़र अनुबंध लिखें:

अनुबंध का प्रश्न इस उदाहरण का निर्णय
सीमा के पार क्या जाता है? आकृति के प्रकार का वर्णन करने वाला संस्करणयुक्त मान
यह प्रतिनिधित्व कौन प्राप्त कर सकता है? केवल स्रोत ऐप
सफल ड्रॉप क्या करता है? संग्रह में नई पहचान वाला एक आइटम जोड़ता है
बदलाव का स्वामित्व किसके पास है? गंतव्य व्यू की संग्रह स्थिति
रद्द या अस्वीकार होने पर क्या होता है? मॉडल में कोई बदलाव नहीं
ड्रॉप का स्थान क्या दर्शाता है? यहाँ कुछ नहीं; स्वीकृत आइटम क्रम से जोड़े जाते हैं

ये ऐप डिज़ाइन के निर्णय हैं। ड्रैग प्रीव्यू पेलोड को दिखाता है, जबकि गंतव्य तय करता है कि वह पेलोड मॉडल को कैसे बदलेगा। इन ज़िम्मेदारियों को अलग रखने से संग्रह के नियम दोबारा लिखे बिना ट्रांसपोर्ट बदला जा सकता है।

अनुबंध व्यक्त करने वाला सबसे छोटा API चुनें

स्थिति शुरुआत यहाँ से करें कारण
SwiftUI स्रोत और गंतव्य, macOS 13 या बाद का संस्करण Transferable, .draggable, और टाइप्ड .dropDestination स्वीकृत मॉडल प्रकार सीधे व्यक्त करें
मौजूदा NSItemProvider एकीकरण .onDrag और .onDrop प्रतिनिधित्व का स्पष्ट पंजीकरण और लोडिंग बनाए रखें
macOS 13 से पुराना लक्ष्य उस लक्ष्य के लिए provider मॉडिफ़ायर की उपलब्धता जाँचें टाइप्ड आधार के लिए macOS 13 चाहिए; नीचे चर्चा में प्रयुक्त provider ओवरलोड के लिए macOS 11 चाहिए
मौजूदा AppKit व्यू माउस इंटरैक्शन का स्वामी है AppKit ड्रैगिंग API व्यू का इवेंट हैंडलिंग और ड्रैगिंग-सत्र का जीवनचक्र बनाए रखें

Apple, Transferable और मूल टाइप्ड ड्रॉप डेस्टिनेशन को macOS 13 से दस्तावेज़ित करता है। provider-आधारित onDrop(of:isTargeted:perform:) ओवरलोड, जो UTType मानों की एक सरणी स्वीकार करता है, macOS 11 से उपलब्ध है। यह मानने के बजाय कि हर ड्रैग-एंड-ड्रॉप API का न्यूनतम संस्करण एक ही है, संबंधित ओवरलोड जाँचें। Transferable, dropDestination(for:action:isTargeted:), onDrop(of:isTargeted:perform:)।

दोनों तरीकों से दृश्यता को अपने ऐप तक सीमित किया जा सकता है। टाइप्ड प्रतिनिधित्व के लिए .visibility(.ownProcess) लागू करें; Apple इस स्तर को केवल स्रोत ऐप के भीतर की दृश्यता के रूप में परिभाषित करता है। इसलिए, केवल इस ट्रांसफ़र को ऐप के भीतर रखने के लिए आइटम प्रोवाइडर चुनना ज़रूरी नहीं है। visibility(_:), ownProcess।

टाइप्ड कॉपी इंटरैक्शन बनाएँ

इस पूर्ण उदाहरण में नए macOS SwiftUI ऐप लक्ष्य की दो Swift फ़ाइलें हैं। परिनियोजन लक्ष्य macOS 13 या बाद का रखें और इस आधार के लिए Swift 5 भाषा मोड चुनें। दूसरी फ़ाइल से जेनरेट किया गया ऐप एंट्री पॉइंट बदलें; केवल एक @main घोषणा रखें। कोड की जाँच की गई है, लेकिन यहाँ इसे कंपाइल नहीं किया गया, क्योंकि उपलब्ध शेल में Swift टूलचेन नहीं है।

पेलोड और उसका कंटेंट प्रकार घोषित करें

लक्ष्य की सूचना प्रॉपर्टी सूची में एक्सपोर्ट किया गया प्रकार घोषित करें। Xcode के प्रॉपर्टी-लिस्ट एडिटर में UTExportedTypeDeclarations को एक ऐरे के रूप में बनाएँ, जिसमें एक डिक्शनरी हो। उस डिक्शनरी में UTTypeIdentifier का मान com.example.shape-transfer, UTTypeDescription का मान Synthetic Shape Transfer, और UTTypeConformsTo को एक ऐसे ऐरे के रूप में सेट करें जिसमें public.data हो। यह पहचानकर्ता केवल उदाहरण के लिए है; अपने वास्तविक ऐप के लिए विशिष्ट पहचानकर्ता चुनें।

Swift घोषणा और प्रॉपर्टी-लिस्ट पहचानकर्ता एक जैसे होने चाहिए। Apple, कस्टम यूनिफ़ॉर्म प्रकारों के लिए संबंधित प्रॉपर्टी-लिस्ट प्रविष्टियाँ आवश्यक बताता है और ऐप द्वारा परिभाषित फ़ॉर्मैट के लिए एक्सपोर्ट की गई घोषणाएँ समझाता है। यह उदाहरण फ़ाइलों के बजाय डेटा ट्रांसफ़र करता है, इसलिए इसमें फ़ाइल नाम एक्सटेंशन नहीं जोड़ा गया है। अपने ऐप के लिए फ़ाइल और डेटा प्रकार परिभाषित करना।

ShapePayload.swift बनाएँ:

import Foundation
import CoreTransferable
import UniformTypeIdentifiers

extension UTType {
    static let shapeTransfer = UTType(
        exportedAs: "com.example.shape-transfer",
        conformingTo: .data
    )
}

enum ShapeKind: String, Codable, Sendable {
    case circle = "Circle"
    case square = "Square"
}

struct ShapePayload: Codable, Sendable, Transferable {
    let version: Int
    let kind: ShapeKind

    static var transferRepresentation: some TransferRepresentation {
        CodableRepresentation(contentType: .shapeTransfer)
            .visibility(.ownProcess)
    }
}

struct PlacedShape: Identifiable {
    let id = UUID()
    let kind: ShapeKind
}

CodableRepresentation, पेलोड और transferable डेटा के बीच रूपांतरण करता है। पेलोड में एक मान होता है, व्यू या म्यूटेबल संग्रह का संदर्भ नहीं। इसकी स्पष्ट Sendable अनुरूपता Swift के उस नियम के अनुसार है कि sendable संरचना के संग्रहित सदस्य भी sendable होने चाहिए। Transferable, Sendable।

जानबूझकर केवल एक प्रतिनिधित्व रखा गया है। टेक्स्ट प्रॉक्सी जोड़ने से आइटम इस्तेमाल करने का एक और तरीका जुड़ता, जिसकी इस अनुबंध में कोई उपयोगिता नहीं है। Apple प्रतिनिधित्वों के क्रम और फ़ॉलबैक फ़ॉर्मैट का दस्तावेज़ देता है; इन्हें तभी जोड़ें जब किसी रिसीवर को वास्तव में इनकी ज़रूरत हो। किसी व्यू को ड्रैग स्रोत बनाना।

पेलोड स्वीकार करें और संग्रह में एक बदलाव लागू करें

ShapeTransferApp.swift बनाएँ:

import SwiftUI

@main
struct ShapeTransferApp: App {
    var body: some Scene {
        WindowGroup {
            ShapeTransferView()
        }
    }
}

@MainActor
struct ShapeTransferView: View {
    @State private var placed: [PlacedShape] = []
    @State private var targeted = false
    @State private var message = "Drag a shape into Collection."

    var body: some View {
        HStack(alignment: .top, spacing: 24) {
            VStack(spacing: 16) {
                Text("Palette").font(.headline)
                paletteItem(.circle)
                paletteItem(.square)
            }
            .frame(width: 160)

            VStack(alignment: .leading, spacing: 12) {
                Text("Collection").font(.headline)
                Text(message)
                ForEach(placed) { item in
                    Label(item.kind.rawValue,
                          systemImage: symbol(for: item.kind))
                }
                Spacer(minLength: 20)
            }
            .padding()
            .frame(width: 300, height: 320, alignment: .topLeading)
            .background(targeted ? Color.blue.opacity(0.15)
                                 : Color.gray.opacity(0.08))
            .contentShape(Rectangle())
            .dropDestination(
                for: ShapePayload.self,
                action: { payloads, _ in
                    guard !payloads.isEmpty,
                          payloads.allSatisfy({ $0.version == 1 })
                    else {
                        message = "Drop rejected: unsupported payload."
                        return false
                    }
                    let additions = payloads.map {
                        PlacedShape(kind: $0.kind)
                    }
                    placed.append(contentsOf: additions)
                    message = "Added \(additions.count) shape(s)."
                    return true
                },
                isTargeted: { targeted = $0 }
            )
        }
        .padding(24)
    }

    private func paletteItem(_ kind: ShapeKind) -> some View {
        Label(kind.rawValue, systemImage: symbol(for: kind))
            .padding()
            .background(Color.gray.opacity(0.12))
            .draggable(ShapePayload(version: 1, kind: kind))
    }

    private func symbol(for kind: ShapeKind) -> String {
        switch kind {
        case .circle: return "circle.fill"
        case .square: return "square.fill"
        }
    }
}

स्पष्ट action: और isTargeted: लेबल मूल टाइप्ड डेस्टिनेशन ओवरलोड चुनते हैं। Apple इसके Boolean परिणाम को सफलता या विफलता के रूप में और इसके लोकेशन आर्ग्युमेंट को गंतव्य व्यू के कोऑर्डिनेट स्पेस में परिभाषित करता है। यहाँ लोकेशन को जानबूझकर इस्तेमाल नहीं किया गया है, क्योंकि संग्रह आकृतियों की स्थिति तय करने के बजाय उन्हें जोड़ता है। dropDestination(for:action:isTargeted:)।

स्वीकृति नियम कुछ भी जोड़ने से पहले डिलीवर किए गए हर पेलोड की जाँच करता है। असमर्थित संस्करण मिलने पर पूरी डिलीवर की गई बैच अस्वीकार हो जाती है; मान्य पेलोड में से हर एक को गंतव्य पर नई पहचान मिलती है। इसलिए पैलेट से वही ड्रैग दोहराने पर एक और कॉपी बनती है। यह नियम क्लोज़र तक पहुँचे मानों पर लागू होता है; डिकोडिंग की विफलताएँ ShapePayload मान बनने से पहले होती हैं।

टार्गेटिंग स्थिति केवल प्रस्तुति बदलती है। इससे आइटम नहीं जोड़े जाते और स्रोत से कुछ नहीं हटता। इस अलगाव से रद्द करने का परिणाम भी सरल रहता है: स्वीकृत ड्रॉप न होने पर संग्रह अपरिवर्तित रहता है।

एकीकरण की माँग होने पर आइटम प्रोवाइडर इस्तेमाल करें

Provider कार्यान्वयन में वही संस्करण और आकृति प्रकार भेजे जा सकते हैं। अनुबंध को ज्यों का त्यों रखें और इन चरणों से ट्रांसपोर्ट बदलें:

  1. .onDrag क्लोज़र में एक NSItemProvider बनाएँ और .ownProcess दृश्यता के साथ उसी कस्टम प्रकार पहचानकर्ता के तहत डेटा प्रतिनिधित्व पंजीकृत करें।
  2. इसके लोड हैंडलर में एक अपरिवर्तनीय पेलोड मान या पहले से तैयार बाइट्स कैप्चर करें। उस मान को एन्कोड करें और प्रतिनिधित्व के पूर्णता हैंडलर को डेटा या त्रुटि के साथ कॉल करें।
  3. .onDrop को कस्टम प्रकार स्वीकार करने के लिए कॉन्फ़िगर करें।
  4. ड्रॉप ऐक्शन के भीतर हर स्वीकृत provider की लोडिंग तुरंत loadDataRepresentation(forTypeIdentifier:completionHandler:) से शुरू करें।
  5. लौटे हुए डेटा को डिकोड करें, संस्करण मान्य करें और परिणाम इकट्ठा करें। आवश्यक परिणाम उपलब्ध होने के बाद ही मुख्य actor पर संग्रह में बदलाव करें।

Apple, providers के लिए स्पष्ट डेटा पंजीकरण और अपने ऐप तक सीमित दृश्यता का दस्तावेज़ देता है। इसकी लोडिंग विधि एसिंक्रोनस होती है और एक प्रोग्रेस ऑब्जेक्ट लौटाती है। मेल खाता प्रकार पहचानकर्ता यह बताता है कि लोड करने की कोशिश की जा सकती है; यह नहीं कि लोडिंग और डिकोडिंग सफल भी होंगे। registerDataRepresentation(forTypeIdentifier:visibility:loadHandler:), NSItemProviderRepresentationVisibility.ownProcess, loadDataRepresentation(forTypeIdentifier:completionHandler:)।

समय से जुड़ी दो सीमाएँ मायने रखती हैं। पहली, Apple कहता है कि provider लोडिंग .onDrop ऐक्शन के भीतर शुरू होनी चाहिए, हालाँकि वह बाद में पूरी हो सकती है। शुरुआती लोड कॉल को किसी दूसरे actor पर बाद के टास्क में ले जाने से यह दस्तावेज़ित आवश्यकता टूटती है। दूसरी, Apple चेतावनी देता है कि प्रतिनिधित्व हैंडलर से एक्सपोर्ट का काम मुख्य actor या मुख्य थ्रेड पर भेजने से ऐप अटक सकता है। एक्सपोर्ट को UI में बदलाव से अलग रखें; इंपोर्ट पूरा होने के बाद संग्रह की स्थिति अपडेट करें। onDrop(of:isTargeted:perform:), onDrag(_:)।

एसिंक्रोनस provider कार्यान्वयन में, लोडिंग का काम स्वीकार करने और मॉडल ट्रांज़ैक्शन पूरा होने के बीच अंतर रखें। इंडेक्स किए गए परिणामों से provider का क्रम बनाए रखें, लोडिंग या डिकोडिंग की विफलताओं की सूचना दें, और यदि आपके अनुबंध में हर आइटम का सफल होना ज़रूरी है तो संग्रह को अपरिवर्तित छोड़ें। फ़िल्टरिंग ऑपरेशन से विफल आइटम चुपचाप न हटाएँ। ये कार्यान्वयन नियम उदाहरण की बैच स्वीकृति नीति से निकलते हैं।

AppKit का इवेंट स्वामित्व और मूव व्यवहार बनाए रखें

यदि कोई मौजूदा AppKit व्यू पहले से माउस इंटरैक्शन का स्वामी है, तो सत्र की शुरुआत वहीं रखें। Apple का beginDraggingSession(with:event:source:) शुरूआती माउस-डाउन इवेंट, इस व्यू के कोऑर्डिनेट का इस्तेमाल करने वाले फ़्रेम के ड्रैगिंग आइटम और NSDraggingSource के अनुरूप स्रोत लेता है। स्रोत कॉलबैक सत्र की शुरुआत, उसकी गतिविधि और पूर्णता या रद्द होने को संभालते हैं। beginDraggingSession(with:event:source:)।

AppKit का प्रतिनिधित्व सीमा भी स्पष्ट है: NSDraggingItem(pasteboardWriter:) के लिए NSPasteboardWriting ऑब्जेक्ट चाहिए। SwiftUI का provider कॉलबैक और AppKit का पेस्टबोर्ड लेखक अलग इंटरफ़ेस हैं। कस्टम AppKit व्यू को SwiftUI में ब्रिज करते समय, AppKit व्यू को अपने इवेंट और सत्र की ज़िम्मेदारी लेने दें; स्वीकृत संग्रह बदलाव साझा मॉडल के स्वामित्व में रखें। init(pasteboardWriter:)।

यह उदाहरण कॉपी करता है। यदि काम बदलकर संग्रहों के बीच मौजूदा आइटम ले जाना हो, तो स्थिर आइटम पहचानकर्ता जोड़ें और हटाने का स्वामी ठीक-ठीक तय करें। साझा स्टोर किसी आइटम को एक बार स्थानांतरित कर सकता है, या गंतव्य आइटम जोड़ सकता है जबकि पुष्टि की गई मूव पूर्णता के बाद स्रोत उसे हटा सकता है। दोनों तरीके एक साथ अपनाने से आइटम दो बार हटने का जोखिम है।

Apple के मौजूदा दस्तावेज़ में macOS 26 के लिए DragConfiguration, सत्र-आधारित टाइप्ड ड्रॉप ओवरलोड और ड्रैग-सत्र ऑब्ज़र्वेशन शामिल हैं। स्रोत समर्थित ऑपरेशन प्रस्तावित करता है, गंतव्य ऑपरेशन चुनता है और सत्र की पूर्णता से स्रोत को परिणाम पर प्रतिक्रिया देने का अवसर मिलता है। इस तरीके को अपनाने से पहले हर सिंबल की SDK उपलब्धता जाँचें। DragConfiguration, dropDestination(for:isEnabled:action:), onDragSessionUpdated(_:)।

पॉइंटर के गंतव्य में प्रवेश करने या प्रीव्यू दिखने पर स्रोत आइटम न हटाएँ। ये घटनाएँ स्वीकृति से पहले होती हैं। अपने ऐप के भीतर मूव के लिए उपयोगी अपरिवर्तनीय नियम यह है कि ट्रांज़ैक्शन पूरा होने पर हर तार्किक आइटम का स्वामी संग्रह केवल एक हो।

दिखाई देने वाले परिणामों से अनुबंध जाँचें

Mac पर कार्यान्वयन जाँचने के लिए यह क्रम अपनाएँ:

  • Circle को Collection में ड्रैग करें: गंतव्य में एक वृत्त जुड़ता है और पैलेट में उसकी प्रविष्टि बनी रहती है।
  • ड्रैग दोहराएँ: गंतव्य में दूसरी, स्वतंत्र पहचान वाली प्रविष्टि जुड़ती है।
  • Square को गंतव्य के बाहर ड्रैग करके छोड़ें: संग्रह में कोई बदलाव नहीं होता।
  • स्रोत पेलोड का संस्करण अस्थायी रूप से 2 करें: गंतव्य अस्वीकृति बताता है और कुछ नहीं जोड़ता; बाद में 1 बहाल करें।
  • Provider कार्यान्वयन में जानबूझकर एन्कोडिंग या लोडिंग त्रुटि लौटाएँ: आंशिक बैच जोड़े बिना विफलता दिखाएँ।
  • मूव कार्यान्वयन में ड्रैग रद्द करें और पुष्टि करें कि मूल आइटम अपने पुराने संग्रह में ही है।

दो पैन वाले इस कॉपी कार्य के लिए टाइप्ड SwiftUI चुनें, जब तक कि किसी ठोस संगतता या एकीकरण आवश्यकता के कारण provider या AppKit की ज़रूरत न हो। टिकाऊ हिस्सा अनुबंध है: सीमा पार एक परिभाषित मान जाता है, गंतव्य उसे मान्य करता है और मॉडल का एक स्वामी स्वीकृत बदलाव लागू करता है।

स्रोत