किसी ठोस सामग्री प्रकार तक सीमित इनिशियलाइज़र जोड़कर SwiftUI जेनेरिक कंपोनेंट को डिफ़ॉल्ट रूप दें। कस्टमाइज़ेशन के लिए अलग बिल्डर इनिशियलाइज़र बनाए रखें। इस चरण-दर-चरण मार्गदर्शिका में macOS विंडो के लिए एक पुनः उपयोग योग्य पैनल बनाया गया है और बताया गया है कि Swift 5.7 का डिफ़ॉल्ट-आर्ग्युमेंट अनुमान इंटरफ़ेस को कब सरल बना सकता है।

वे दो कॉल परिभाषित करें जिनका आपका कंपोनेंट समर्थन करे

पाठक का काम सीधा है: ऐसा डेस्कटॉप पैनल बनाना जो केवल शीर्षक देने पर ही पूरा दिखे, साथ ही किसी दूसरी स्क्रीन को अपनी सामग्री देने की अनुमति भी हो। दोनों ही स्थितियों में आसपास का शीर्षक, रिक्ति और बॉर्डर एक जैसे रहने चाहिए।

वांछित इंटरफ़ेस में डिफ़ॉल्ट प्रस्तुति के लिए DesktopPanel(title: "Workspace") और कस्टम सामग्री के लिए DesktopPanel(title: "Workspace") { Text("Three sample items") } का उपयोग होता है। ये कॉल एक ही जेनेरिक कंपोनेंट के अलग-अलग स्पेशलाइज़ेशन बनाते हैं।

Swift के जेनेरिक पैरामीटर ठोस प्रकारों के स्थान पर आते हैं। Content: View जैसी बाधा अनुरूपता अनिवार्य करती है; Content == DefaultPanelContent जैसी समान-प्रकार शर्त एक विशिष्ट प्रकार चुनती है। भाषा संदर्भ इन दोनों प्रकार की शर्तों और बाधाओं के ज़रिए इनिशियलाइज़र ओवरलोड चुनने की प्रक्रिया का दस्तावेज़ देता है। जेनेरिक पैरामीटर और आर्ग्युमेंट

इससे कंपोनेंट की दो स्पष्ट ज़िम्मेदारियाँ तय होती हैं: उसका body पैनल का साझा रूप तय करता है, और उसका इनिशियलाइज़र यह तय करता है कि उसके भीतर कौन-सी सामग्री रखी जाएगी। डिफ़ॉल्ट सामग्री को एक नाम वाला व्यू प्रकार दिया जाता है, ताकि सुविधा वाला इनिशियलाइज़र एक स्थिर और आसानी से समझ आने वाले स्पेशलाइज़ेशन का संदर्भ ले सके।

पूरा macOS उदाहरण बनाएँ

इस मार्गदर्शिका के लिए macOS 11 या उसके बाद के डिप्लॉयमेंट टारगेट और Swift 5.7 या नए कंपाइलर वाले macOS SwiftUI ऐप टारगेट का उपयोग करें। macOS का यह न्यूनतम संस्करण नीचे इस्तेमाल किए गए App और WindowGroup ढाँचे के लिए पर्याप्त है; Apple WindowGroup को विंडो के व्यू पदानुक्रम के लिए सीन कंटेनर के रूप में दस्तावेज़ित करता है। WindowGroup

जनरेट किए गए ऐप एंट्री पॉइंट को इस पूरी सोर्स फ़ाइल से बदलें, और यदि जनरेट की गई ContentView.swift में एक और ContentView घोषित है, तो उसे हटाएँ। टारगेट में ठीक एक @main ऐप डिक्लेरेशन रखें। नीचे दिखाए गए सभी मान केवल उदाहरण के लिए स्थानीय रूप से बनाए गए हैं।

import SwiftUI

struct DefaultPanelContent: View {
    var body: some View {
        VStack(alignment: .leading, spacing: 6) {
            Text("Sample workspace")
                .font(.headline)
            Text("Choose a layout to begin.")
                .foregroundColor(.secondary)
        }
    }
}

struct DesktopPanel<Content: View>: View {
    private let title: String
    private let content: Content

    init(
        title: String,
        @ViewBuilder content: () -> Content
    ) {
        self.title = title
        self.content = content()
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            Text(title)
                .font(.title2)

            Divider()

            content
        }
        .padding(16)
        .frame(maxWidth: .infinity, alignment: .leading)
        .background(Color.gray.opacity(0.08))
        .cornerRadius(10)
        .overlay(
            RoundedRectangle(cornerRadius: 10)
                .stroke(Color.gray, lineWidth: 1)
        )
    }
}

extension DesktopPanel where Content == DefaultPanelContent {
    init(title: String) {
        self.init(title: title) {
            DefaultPanelContent()
        }
    }
}

struct ContentView: View {
    var body: some View {
        VStack(alignment: .leading, spacing: 20) {
            DesktopPanel(title: "Default panel")

            DesktopPanel(title: "Custom panel") {
                HStack {
                    Text("Sample items")
                    Spacer()
                    Text("3")
                        .font(.headline)
                }

                Text("Layout: Compact")
                    .foregroundColor(.secondary)
            }
        }
        .padding(24)
        .frame(minWidth: 420, minHeight: 300)
    }
}

@main
struct PanelExampleApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

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

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

सीमित इनिशियलाइज़र जेनेरिक प्रकार कैसे चुनता है

कस्टम इनिशियलाइज़र ऐसा क्लोज़र लेता है जो Content लौटाता है। content() को कॉल करने पर पैनल में रखा जाने वाला मान बनता है। क्लोज़र का उपयोग इनिशियलाइज़ेशन के दौरान हो जाता है, इसलिए यह इम्प्लिमेंटेशन उसे स्टोर नहीं करता और @escaping की आवश्यकता नहीं होती।

बिना बिल्डर वाली कॉल के लिए, एक्सटेंशन init(title:) को केवल तभी उपलब्ध कराता है जब Content, DefaultPanelContent हो। यह शर्त छूटी हुई प्रकार-संबंधी जानकारी देती है। फिर उसका इम्प्लिमेंटेशन बिल्डर इनिशियलाइज़र को सौंप देता है, जिससे प्रॉपर्टी का इनिशियलाइज़ेशन एक ही जगह रहता है।

इससे मिलने वाले डिफ़ॉल्ट मान का प्रकार DesktopPanel<DefaultPanelContent> है। कस्टम पैनल का सामग्री प्रकार उसके क्लोज़र से अनुमानित होता है। कॉल करने वालों को यह संभावित रूप से जटिल प्रकार लिखने की ज़रूरत नहीं पड़ती।

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

बिल्डर एट्रिब्यूट उदाहरण में कई स्टेटमेंट वाले कस्टम क्लोज़र को सक्षम करता है। इससे HStack और उसके बाद वाला Text बनाई जा रही सामग्री का हिस्सा बन सकते हैं; यह कंपोनेंट की अपनी Content: View शर्त की जगह नहीं लेता। Apple दस्तावेज़ित करता है कि बिल्डर कई एक्सप्रेशन और सशर्त सामग्री को कैसे संभालता है। ViewBuilder

डिफ़ॉल्ट बिल्डर इनिशियलाइज़र ओवरलोड की जगह कब ले सकता है?

Swift 5.7 में SE-0347 लागू हुआ, जो छोड़े गए आर्ग्युमेंट के ठोस डिफ़ॉल्ट एक्सप्रेशन से जेनेरिक प्रकार का अनुमान लगाने की अनुमति देता है। इस कंपोनेंट के लिए यह एक छोटा विकल्प देता है। SE-0347: डिफ़ॉल्ट एक्सप्रेशन से प्रकार का अनुमान

Swift 5.7 या नए कंपाइलर के लिए, DesktopPanel के अंदर बिल्डर इनिशियलाइज़र को नीचे दिए अंश से बदलें और पूरा सीमित एक्सटेंशन हटा दें। पूरे उदाहरण से इम्पोर्ट, स्टोर्ड प्रॉपर्टीज़, DefaultPanelContent, body और ऐप कोड बनाए रखें।

// Replacement initializer inside DesktopPanel.
init(
    title: String,
    @ViewBuilder content: () -> Content = {
        DefaultPanelContent()
    }
) {
    self.title = title
    self.content = content()
}

अब क्लोज़र छोड़ने पर Content का अनुमान DefaultPanelContent() से लगाया जाता है। क्लोज़र देने पर उसका अनुमान उस आर्ग्युमेंट से लगाया जाता है। यह डिफ़ॉल्ट-एक्सप्रेशन से प्रकार का अनुमान है, कंपोनेंट डिक्लेरेशन में डिफ़ॉल्ट प्रकार का आर्ग्युमेंट नहीं।

प्रस्ताव इस अनुमान पर सीमाएँ लगाता है: प्रभावित जेनेरिक पैरामीटर पैरामीटर सूची में केवल एक स्थान पर आना चाहिए, और संबंधित समान-प्रकार शर्तों को उसके नियम पूरे करने चाहिए। यहाँ Content केवल बिल्डर पैरामीटर में आता है। किसी दूसरे इनिशियलाइज़र पैरामीटर में उसका फिर से उपयोग करने से यह तरीका अमान्य हो सकता है। SE-0347 का विस्तृत डिज़ाइन

जब डिफ़ॉल्ट सामग्री को इनिशियलाइज़र के इनपुट की ज़रूरत हो, तो ओवरलोड चुनें। उदाहरण के लिए, title से निकला डिफ़ॉल्ट सबटाइटल init(title:) के भीतर बनाया जा सकता है और बिल्डर इनिशियलाइज़र को दिया जा सकता है। डिफ़ॉल्ट आर्ग्युमेंट एक्सप्रेशन दिए गए title को ऐसे कैप्चर नहीं कर सकता, मानो वह उसी इनिशियलाइज़र के बॉडी के भीतर बनाया गया क्लोज़र हो।

कंपोनेंट की आवश्यकता उपयुक्त इंटरफ़ेस
तय डिफ़ॉल्ट सामग्री और Swift 5.7+ कंपाइलर डिफ़ॉल्ट वाला बिल्डर पैरामीटर संक्षिप्त है।
अन्य इनिशियलाइज़र इनपुट से बनाई गई डिफ़ॉल्ट सामग्री सीमित इनिशियलाइज़र का उपयोग करें और उसके भीतर मान बनाएँ।
कई नामित डिफ़ॉल्ट प्रस्तुतियाँ स्पष्ट लेबल और शर्तों वाले इनिशियलाइज़र ओवरलोड का उपयोग करें।
डिफ़ॉल्ट स्पेशलाइज़ेशन के लिए स्पष्ट स्टोर्ड प्रकार DesktopPanel<DefaultPanelContent> लिखें या टाइप एलियास बनाएँ।

इस इंटरफ़ेस को struct DesktopPanel<Content: View = DefaultPanelContent> के रूप में लिखने की कोशिश न करें। Swift का मौजूदा जेनेरिक-पैरामीटर ग्रामर डिफ़ॉल्ट-प्रकार का यह सिंटैक्स उपलब्ध नहीं कराता। यहाँ समर्थित तरीके सीमित इनिशियलाइज़ेशन और डिफ़ॉल्ट-एक्सप्रेशन से प्रकार का अनुमान हैं। जेनेरिक पैरामीटर और आर्ग्युमेंट

बिल्डर के संस्करणों में इंटरफ़ेस बनाए रखें

Apple का मौजूदा दस्तावेज़ Xcode 26 और उससे पहले के संस्करणों को Xcode 27 और उसके बाद के संस्करणों से अलग बताता है। नए टूलचेन में ViewBuilder प्रकार-निरपेक्ष सामग्री बनाता है; Apple इस एकीकृत व्यवहार के लिए ContentBuilder प्रस्तुत करता है, जो ViewBuilder का टाइप एलियास है। यह अंतर बिल्ड टूलचेन पर निर्भर है, ऐप के डिप्लॉयमेंट टारगेट पर मात्र नहीं। ViewBuilder, ContentBuilder

यह उदाहरण पुराने टूलचेन के साथ संगतता के लिए @ViewBuilder बनाए रखता है। Xcode 27 या उसके बाद के संस्करण में नया नाम अपनाते समय, उस पैरामीटर एट्रिब्यूट को @ContentBuilder से बदलें। Content: View बनाए रखें: पैनल को अब भी ऐसी सामग्री चाहिए जिसे वह व्यू पदानुक्रम में रख सके।

डिफ़ॉल्ट स्पेशलाइज़ेशन को नेस्टेड स्टैक और टपल प्रकार के रूप में लिखने से बचें। Apple का माइग्रेशन नोट बताता है कि कई एक्सप्रेशन वाले बिल्डर में TupleView से TupleContent में बदलाव उन शर्तों को तोड़ सकता है जो इन जनरेट हुई संरचनाओं का नाम लेती हैं। उदाहरण इसके बजाय एक्सटेंशन को DefaultPanelContent तक सीमित करता है; उसका आंतरिक लेआउट body: some View के पीछे रहता है। TN3211: State और ContentBuilder के लिए SwiftUI सोर्स असंगतियों का समाधान

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

लेआउट बदलने से पहले अनुबंध की जाँच करें

अगर कंपोनेंट कंपाइल नहीं होता, तो पहले पहचानें कि इंटरफ़ेस के किस हिस्से में प्रकार-संबंधी जानकारी नहीं है। पैडिंग बदलने या सब कुछ किसी दूसरे स्टैक में रखने से छूटा हुआ जेनेरिक स्पेशलाइज़ेशन ठीक नहीं होगा।

  1. बिना बिल्डर वाली कॉल Content का अनुमान नहीं लगा पाती। पुष्टि करें कि सीमित init(title:) मौजूद है, या डिफ़ॉल्ट वाले इनिशियलाइज़र को Swift 5.7 या नए संस्करण के कंपाइलर से कंपाइल किया जा रहा है।
  2. ठोस डिफ़ॉल्ट को Content में असाइन नहीं किया जा सकता। जाँचें कि असाइनमेंट सीमित एक्सटेंशन के भीतर हो रहा है। केवल View के अनुरूप होना DefaultPanelContent को हर संभावित Content के साथ विनिमेय नहीं बनाता।
  3. कई कस्टम एक्सप्रेशन अस्वीकार किए जाते हैं। पुष्टि करें कि क्लोज़र पैरामीटर पर बिल्डर एट्रिब्यूट लगा है।
  4. स्पष्ट प्रकार कस्टम सामग्री अस्वीकार करता है। DesktopPanel<DefaultPanelContent> का एनोटेशन जानबूझकर सामग्री प्रकार तय करता है। अगर कॉल को अलग स्पेशलाइज़ेशन का अनुमान लगाना चाहिए, तो यह एनोटेशन हटाएँ।
  5. टूलचेन अपग्रेड से टपल-आधारित शर्त टूट जाती है। जनरेट हुई बिल्डर संरचना पर निर्भरता हटाकर नाम वाला डिफ़ॉल्ट व्यू अपनाएँ, या संबंधित शर्त के लिए Apple के दस्तावेज़ित माइग्रेशन का पालन करें।

अंत में, बिल्डर को हल्का रखें। इस इम्प्लिमेंटेशन में यह पैनल के इनिशियलाइज़ होने पर हर बार चलता है; यह एक ही बार काम होने की गारंटी नहीं देता। Apple बताता है कि SwiftUI व्यू को फिर से इनिशियलाइज़ कर सकता है और उनके इनिशियलाइज़र में भारी काम से बचने की सलाह देता है। कस्टम व्यू घोषित करना

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

स्रोत