क्या SwiftUI के DSL को सक्षम करता है?




(2)

ऐसा लगता है जैसे Apple का नया SwiftUI फ्रेमवर्क एक नए तरह के सिंटैक्स का उपयोग करता है जो प्रभावी रूप से एक SwiftUI बनाता है, लेकिन एक और सिंटैक्स है:

var body: some View {
    VStack(alignment: .leading) {
        Text("Hello, World") // No comma, no separator ?!
        Text("Hello World!")
    }
}

इस वाक्यविन्यास को वास्तव में क्या है , इससे निपटने की कोशिश करते हुए , मुझे पता चला कि यहाँ इस्तेमाल किया गया VStack initializer एक बंद प्रकार का () -> Content दूसरे पैरामीटर के रूप में () -> Content लेता है, जहाँ Content एक सामान्य पैरामीटर है जो View अनुरूप है कि बंद के माध्यम से अनुमान लगाया गया है । यह जानने के लिए कि किस प्रकार की Content का अनुमान है, मैंने इसकी कार्यक्षमता बनाए रखते हुए कोड को थोड़ा बदल दिया:

var body: some View {
    let test = VStack(alignment: .leading) {
        Text("Hello, World")
        Text("Hello World!")
    }

    return test
}

इसके साथ, test से ही पता चलता है कि टाइप VStack<TupleView<(Text, Text)>> , अर्थात Content TupleView<Text, Text>TupleView देखते हुए, मैंने पाया कि यह SwiftUI से उत्पन्न होने SwiftUI एक रैपर प्रकार है जिसे केवल उस रैप को पास करके आरंभीकृत किया जा सकता है जिसे इसे लपेटना चाहिए।

सवाल

अब मैं सोच रहा हूँ कि कैसे दुनिया में इस उदाहरण में दो Text उदाहरण एक TupleView<(Text, Text)> परिवर्तित हो जाते हैं। क्या यह SwiftUI में हैक किया SwiftUI और इसलिए नियमित स्विफ्ट सिंटैक्स अमान्य है? TupleView एक SwiftUI प्रकार होने के कारण इस धारणा का समर्थन करता है। या यह वैध स्विफ्ट सिंटैक्स है? यदि हाँ, तो कोई इसे SwiftUI बाहर कैसे उपयोग कर सकता है ?


DSLs के बारे में अनुभाग में WWF वीडियो में व्हाट्स न्यू में एक समरूप चीज का वर्णन किया गया है (~ 31: 15 से शुरू होता है)। संकलक द्वारा विशेषता की व्याख्या की जाती है और संबंधित कोड में इसका अनुवाद किया जाता है:


जैसा कि मार्टिन कहते हैं , अगर आप VStack के developer.apple.com/documentation/swiftui/vstack/3278367-init के दस्तावेज़ीकरण को देखते हैं, तो आप देख सकते हैं कि content: पैरामीटर में विशेषता है @ViewBuilder :

init(alignment: HorizontalAlignment = .center, spacing: Length? = nil,
     @ViewBuilder content: () -> Content)

यह विशेषता developer.apple.com/documentation/swiftui/viewbuilder प्रकार को संदर्भित करती है, जिसे यदि आप उत्पन्न इंटरफ़ेस को देखते हैं, तो यह दिखता है:

@_functionBuilder public struct ViewBuilder {

    /// Builds an empty view from an block containing no statements, `{ }`.
    public static func buildBlock() -> EmptyView

    /// Passes a single view written as a child view (e..g, `{ Text("Hello") }`)
    /// through unmodified.
    public static func buildBlock(_ content: Content) -> Content 
      where Content : View
}

@_functionBuilder विशेषता " फ़ंक्शन बिल्डरों " नामक एक अनौपचारिक विशेषता का एक हिस्सा है, जिसे यहां स्विफ्ट विकास पर पिच किया गया है , और स्विफ्ट के संस्करण के लिए विशेष रूप से कार्यान्वित किया गया है जो कि Xcode 11 के साथ जहाजों, इसे स्विफ्टयूआई में उपयोग करने की अनुमति देता है।

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

  • एनोटेट किए गए कार्यों के लिए, कोड का ब्लॉक जो रूपांतरित हो जाता है, वह है कार्यान्वयन।
  • एनोटेट कम्प्यूटेड संपत्तियों के लिए, कोड का ब्लॉक जो रूपांतरित हो जाता है, वह है।
  • फ़ंक्शन प्रकार के एनोटेट किए गए मापदंडों के लिए, कोड का ब्लॉक जो रूपांतरित हो जाता है, वह कोई क्लोजर एक्सप्रेशन है जो इसे (यदि कोई हो) पास किया जाता है।

जिस तरह से एक फ़ंक्शन बिल्डर कोड को परिवर्तित करता है, उसे बिल्डर तरीकों जैसे कि buildBlock कार्यान्वयन से परिभाषित किया जाता है, जो अभिव्यक्तियों का एक सेट लेता है और उन्हें एक एकल मूल्य में समेकित करता है।

उदाहरण के लिए, ViewBuilder 1 से 10 के लिए buildBlock करता है, एक पैरामीटर में कई दृश्य समेकित करते हुए, ViewBuilder मापदंडों को View :

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension ViewBuilder {

    /// Passes a single view written as a child view (e..g, `{ Text("Hello") }`)
    /// through unmodified.
    public static func buildBlock<Content>(_ content: Content)
       -> Content where Content : View

    public static func buildBlock<C0, C1>(_ c0: C0, _ c1: C1) 
      -> TupleView<(C0, C1)> where C0 : View, C1 : View

    public static func buildBlock<C0, C1, C2>(_ c0: C0, _ c1: C1, _ c2: C2)
      -> TupleView<(C0, C1, C2)> where C0 : View, C1 : View, C2 : View

    // ...
}

इससे VStack के इनिशियलाइज़र को बंद करने के लिए पास किए गए व्यू एक्सप्रेशन का एक सेट VStack में तब्दील होने की buildBlock देता है जो समान संख्या में तर्क देता है। उदाहरण के लिए:

struct ContentView : View {
  var body: some View {
    VStack(alignment: .leading) {
      Text("Hello, World")
      Text("Hello World!")
    }
  }
}

एक कॉल में परिवर्तित हो जाता है buildBlock(_:_:) :

struct ContentView : View {
  var body: some View {
    VStack(alignment: .leading) {
      ViewBuilder.buildBlock(Text("Hello, World"), Text("Hello World!"))
    }
  }
}

अपारदर्शी परिणाम के परिणामस्वरूप some View TupleView<(Text, Text)> संतुष्ट हो रहे हैं।

आप ध्यान दें कि ViewBuilder केवल 10 मापदंडों तक buildBlock को परिभाषित करता है, इसलिए यदि हम 11 buildBlock को परिभाषित करने का प्रयास करते हैं:

  var body: some View {
    // error: Static member 'leading' cannot be used on instance of
    // type 'HorizontalAlignment'
    VStack(alignment: .leading) {
      Text("Hello, World")
      Text("Hello World!")
      Text("Hello World!")
      Text("Hello World!")
      Text("Hello World!")
      Text("Hello World!")
      Text("Hello World!")
      Text("Hello World!")
      Text("Hello World!")
      Text("Hello World!")
      Text("Hello World!")
    }
  }

हमें एक संकलक त्रुटि मिलती है, क्योंकि कोड के इस ब्लॉक को संभालने के लिए कोई बिल्डर विधि नहीं है (ध्यान दें कि क्योंकि यह सुविधा अभी भी एक कार्य-प्रगति है, इसके चारों ओर त्रुटि संदेश उतना उपयोगी नहीं होगा)।

हकीकत में, मुझे विश्वास नहीं है कि लोग इस प्रतिबंध में भाग लेंगे, जो अक्सर उदाहरण के लिए, उपरोक्त उदाहरण के बजाय ForEach दृश्य का उपयोग करके बेहतर सेवा की ForEach :

  var body: some View {
    VStack(alignment: .leading) {
      ForEach(0 ..< 20) { i in
        Text("Hello world \(i)")
      }
    }
  }

यदि आपको 10 से अधिक वैधानिक रूप से परिभाषित विचारों की आवश्यकता है, तो आप Group दृश्य का उपयोग करके इस प्रतिबंध को आसानी से हल कर सकते हैं:

  var body: some View {
    VStack(alignment: .leading) {
      Group {
        Text("Hello world")
        // ...
        // up to 10 views
      }
      Group {
        Text("Hello world")
        // ...
        // up to 10 more views
      }
      // ...
    }

ViewBuilder भी अन्य समारोह बिल्डर तरीकों को लागू करता है:

extension ViewBuilder {
    /// Provides support for "if" statements in multi-statement closures, producing
    /// ConditionalContent for the "then" branch.
    public static func buildEither<TrueContent, FalseContent>(first: TrueContent)
      -> ConditionalContent<TrueContent, FalseContent>
           where TrueContent : View, FalseContent : View

    /// Provides support for "if-else" statements in multi-statement closures, 
    /// producing ConditionalContent for the "else" branch.
    public static func buildEither<TrueContent, FalseContent>(second: FalseContent)
      -> ConditionalContent<TrueContent, FalseContent>
           where TrueContent : View, FalseContent : View
}

यदि यह कथन को संभालने की क्षमता देता है:

  var body: some View {
    VStack(alignment: .leading) {
      if .random() {
        Text("Hello World!")
      } else {
        Text("Goodbye World!")
      }
      Text("Something else")
    }
  }

जो रूपांतरित हो जाता है:

  var body: some View {
    VStack(alignment: .leading) {
      ViewBuilder.buildBlock(
        .random() ? ViewBuilder.buildEither(first: Text("Hello World!"))
                  : ViewBuilder.buildEither(second: Text("Goodbye World!")),
        Text("Something else")
      )
    }
  }

(निरर्थक 1-तर्क कॉलिंग ViewBuilder.buildBlock को स्पष्टता के लिए कॉल करता है)।







swiftui