SwiftUI - पर्यावरणओबजेक्ट को व्यू मॉडल में कैसे पास करें?


16

मैं एक EnvironmentObject बनाना चाह रहा हूँ जिसे व्यू मॉडल (केवल दृश्य नहीं) द्वारा एक्सेस किया जा सकता है।

पर्यावरण ऑब्जेक्ट एप्लिकेशन सत्र डेटा, जैसे लॉग इन, एक्सेस टोकन आदि को ट्रैक करता है, इस डेटा को इस एनवायरनमेंट ऑबजेक्ट से डेटा पास करने के लिए एपीआई कॉलिंग की अनुमति देने के लिए दृश्य मॉडल (या सेवा वर्ग जहां आवश्यक हो) में पारित किया जाएगा।

मैंने सत्र मॉडल में दृश्य मॉडल वर्ग के प्रारंभकर्ता से दृश्य में पास करने की कोशिश की है, लेकिन एक त्रुटि मिलती है।

मैं स्विफ्टयूआई का उपयोग करके एन्वायर्नमेंटऑब्जेक्ट को व्यू मॉडल में कैसे एक्सेस / पास कर सकता हूं?

परीक्षण परियोजना के लिए लिंक देखें: https://gofile.io/?c=vgHLVx


ईओ के रूप में दृश्यमॉडल पास क्यों नहीं?
ई.काम्स

शीर्ष पर लगता है, कई दृश्य मॉडल होंगे, मैंने जो अपलोड किया है वह सिर्फ एक सरल उदाहरण है
माइकल

2
मुझे यकीन नहीं है कि इस सवाल को क्यों नकार दिया गया, मैं भी यही सोच रहा हूं। मैंने जो किया है, उसका उत्तर दूंगा, उम्मीद है कि कोई और व्यक्ति कुछ बेहतर कर सकता है।
माइकल ओजेरानस्की

2
@ ई.कॉम्स मुझे उम्मीद थी कि पर्यावरणविद्या आम तौर पर एक वस्तु होगी। मैं कई काम जानता हूँ, यह एक कोड गंध की तरह लगता है जैसे उन्हें विश्व स्तर पर सुलभ बनाने के लिए।
माइकल ओजेरानस्की

@ मायकिल क्या आपको इसका हल भी मिला?
ब्रेट

जवाबों:


3

मैं एक ViewModel नहीं करने के लिए चुनते हैं। (शायद एक नए पैटर्न के लिए समय?)

मैंने अपनी परियोजना को कुछ RootViewऔर बच्चे के विचारों के साथ सेटअप किया है । मैं पर्यावरण ऑब्जेक्ट के रूप में RootViewएक Appवस्तु के साथ अपना सेटअप करता हूं । इसके बजाय ViewModel मॉडल तक पहुँचने के लिए, मेरे सभी दृष्टिकोण अनुप्रयोग पर वर्गों का उपयोग। ViewModel लेआउट का निर्धारण करने के बजाय, दृश्य पदानुक्रम लेआउट निर्धारित करता है। कुछ ऐप के लिए अभ्यास करने से, मैंने पाया है कि मेरे विचार छोटे और विशिष्ट हैं। एक सरलीकरण के रूप में:

class App {
   @Published var user = User()

   let networkManager: NetworkManagerProtocol
   lazy var userService = UserService(networkManager: networkManager)

   init(networkManager: NetworkManagerProtocol) {
      self.networkManager = networkManager
   }

   convenience init() {
      self.init(networkManager: NetworkManager())
   }
}
struct RootView {
    @EnvironmentObject var app: App

    var body: some View {
        if !app.user.isLoggedIn {
            LoginView()
        } else {
            HomeView()
        }
    }
}
struct HomeView: View {
    @EnvironmentObject var app: App

    var body: some View {
       VStack {
          Text("User name: \(app.user.name)")
          Button(action: { app.userService.logout() }) {
             Text("Logout")
          }
       }
    }
}

अपने पूर्वावलोकन में, मैं एक को शुरू करता हूं MockAppजो एक उपवर्ग है App। MockApp Mocked ऑब्जेक्ट के साथ निर्दिष्ट इनिशियलाइज़र्स को इनिशियलाइज़ करता है। यहाँ UserService को मज़ाक करने की ज़रूरत नहीं है, लेकिन डेटासोर्स (यानी NetworkManagerProtocol) करता है।

struct HomeView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            HomeView()
                .environmentObject(MockApp() as App) // <- This is needed for EnvironmentObject to treat the MockApp as an App Type
        }
    }

}

बस एक ध्यान दें: मुझे लगता है कि चाइनिंग से बचना बेहतर है app.userService.logout()userServiceनिजी होना चाहिए और केवल ऐप क्लास के अंदर से ही जाना चाहिए। उपरोक्त कोड इस तरह दिखना चाहिए: Button(action: { app.logout() })और लॉगआउट फ़ंक्शन फिर सीधे कॉल करेगा userService.logout()
pawello2222

@ pawello2222 यह बेहतर नहीं है, यह बिना किसी लाभ के सिर्फ मुखौटा पैटर्न है, लेकिन आप अपनी इच्छानुसार कर सकते हैं।
माइकल ओज़ेराँस्की

3

आपको नहीं करना चाहिए यह एक आम गलत धारणा है कि SwiftUI MVVM के साथ सबसे अच्छा काम करती है।

MVVM का SwfitUI में कोई स्थान नहीं है। आप पूछ रहे हैं कि क्या आप आयत को हिला सकते हैं

एक त्रिकोण आकार फिट। यह फिट नहीं होगा।

आइए कुछ तथ्यों और कदम से कदम मिलाकर चलते हैं:

  1. ViewModel MVVM में एक मॉडल है।

  2. MVVM मान प्रकार (उदाहरण के लिए जावा में ऐसी कोई चीज) नहीं लेता है।

  3. एक मान प्रकार मॉडल (राज्य के बिना मॉडल) को संदर्भ से अधिक सुरक्षित माना जाता है

    टाइप मॉडल (राज्य के साथ मॉडल) अपरिवर्तनीयता के अर्थ में।

अब, MVVM को आपको इस तरह से एक मॉडल स्थापित करने की आवश्यकता होती है, जब भी यह बदलता है, इसे

अद्यतन कुछ पूर्व-निर्धारित तरीके से देखें। इसे बाइंडिंग के रूप में जाना जाता है।

बंधन के बिना, आपको चिंताओं का अच्छा पृथक्करण नहीं होगा, उदाहरण के लिए; बाहर निकालकर

मॉडल और संबंधित राज्य और उन्हें देखने से अलग रखते हैं।

ये दो चीजें हैं जो अधिकांश iOS MVVM डेवलपर्स विफल हैं:

  1. पारंपरिक जावा अर्थों में iOS का कोई "बाध्यकारी" तंत्र नहीं है।

    कुछ लोग सिर्फ बाध्यकारी को अनदेखा करेंगे, और किसी वस्तु को देखने के लिए सोचेंगे

    स्वचालित रूप से सब कुछ हल करता है; कुछ केवीओ-आधारित आरएक्स और पेश करेंगे

    MVVM चीजों को सरल बनाने के लिए माना जाता है जब सब कुछ जटिल।

  2. राज्य के साथ मॉडल अभी बहुत खतरनाक है

    क्योंकि MVVM ViewModel पर बहुत अधिक जोर देता है, राज्य प्रबंधन पर बहुत कम

    और नियंत्रण में सामान्य विषयों; अधिकांश डेवलपर्स समाप्त हो जाते हैं

    राज्य के साथ एक मॉडल के बारे में सोचना जो दृश्य को अद्यतन करने के लिए उपयोग किया जाता है पुन: प्रयोज्य और

    परीक्षण करने योग्य

    यही कारण है कि स्विफ्ट पहली जगह में मूल्य प्रकार का परिचय देता है; बिना एक मॉडल

    राज्य।

अब आपके प्रश्न के लिए: आप पूछते हैं कि क्या आपके ViewModel के पास EnvironmentObject (EO) तक पहुंच हो सकती है?

आपको नहीं करना चाहिए क्योंकि SwiftUI में एक मॉडल जो अपने आप व्यू के अनुरूप है

ईओ के संदर्भ में। उदाहरण के लिए,

struct Model: View {
    @EnvironmentObject state: State
    // automatic binding in body
    var body: some View {...}
}

मुझे उम्मीद है कि लोग इस बात की सराहना कर सकते हैं कि कॉम्पैक्ट एसडीके को कैसे डिज़ाइन किया गया है।

SwiftUI में, MVVM स्वचालित है । एक अलग ViewModel ऑब्जेक्ट की कोई आवश्यकता नहीं है

यह मैन्युअल रूप से देखने के लिए बाध्य करता है जिसके लिए एक EO संदर्भ की आवश्यकता होती है।

इसके बाद के संस्करण कोड है MVVM। उदाहरण के लिए, देखने के लिए बाध्यकारी के साथ एक मॉडल।

लेकिन क्योंकि मॉडल वैल्यू टाइप है, इसलिए मॉडल और स्टेट को रिफैक्ट करने की बजाय

मॉडल देखें, आप नियंत्रण से बाहर हैं (उदाहरण के लिए, प्रोटोकॉल एक्सटेंशन में)।

यह आधिकारिक एसडीके है, जो कि केवल फीचर के बजाय, भाषा की विशेषता के लिए डिजाइन पैटर्न को अपनाता है

इसे लागू करना। आकार से अधिक पदार्थ।

अपने समाधान को देखें, आपको सिंगलटन का उपयोग करना होगा जो मूल रूप से वैश्विक है। आप

यह जानना चाहिए कि बिना सुरक्षा के वैश्विक रूप से कहीं भी पहुंचना कितना खतरनाक है

अपरिवर्तनीयता, जो आपके पास नहीं है क्योंकि आपको संदर्भ प्रकार मॉडल का उपयोग करना है!

टी एल; डॉ

आप SwiftUI में जावा तरीके से MVVM नहीं करते हैं। और यह करने के लिए स्विफ्ट-वाई रास्ता कोई जरूरत नहीं है

यह करने के लिए, यह पहले से ही अंतर्निहित है।

आशा है कि अधिक डेवलपर इसे देखते हैं क्योंकि यह एक लोकप्रिय प्रश्न लगता है।


1

नीचे दिया गया दृष्टिकोण जो मेरे लिए काम करता है। Xcode 11.1 के साथ शुरू किए गए कई समाधानों के साथ परीक्षण किया गया।

जिस तरह से पर्यावरण ओब्जेक्ट को देखने, सामान्य स्कीमा में इंजेक्ट किया गया है, उससे उत्पन्न हुई समस्या

SomeView().environmentObject(SomeEO())

यानी, पहली बार बनाया गया दृश्य, दूसरी निर्मित पर्यावरणीय वस्तु पर, तीसरे पर्यावरणीय वस्तु को देखने में इंजेक्ट किया गया

इस प्रकार अगर मुझे पर्यावरण निर्माता को देखने के लिए / सेटअप व्यू मॉडल बनाने की आवश्यकता है तो वहां पर्यावरण वस्तु अभी तक मौजूद नहीं है।

समाधान: इसके अलावा सब कुछ तोड़ दें और स्पष्ट निर्भरता इंजेक्शन का उपयोग करें

यहाँ यह कोड में कैसे दिखता है (सामान्य स्कीमा)

// somewhere, say, in SceneDelegate

let someEO = SomeEO()                            // create environment object
let someVM = SomeVM(eo: someEO)                  // create view model
let someView = SomeView(vm: someVM)              // create view 
                   .environmentObject(someEO)

यहां कोई भी व्यापार बंद नहीं है, क्योंकि ViewModel और EnvironmentObject डिजाइन, संदर्भ-प्रकार (वास्तव में ObservableObject) से हैं, इसलिए मैं यहां से गुजरता हूं और केवल संदर्भ (उर्फ पॉइंटर्स)।

class SomeEO: ObservableObject {
}

class BaseVM: ObservableObject {
    let eo: SomeEO
    init(eo: SomeEO) {
       self.eo = eo
    }
}

class SomeVM: BaseVM {
}

class ChildVM: BaseVM {
}

struct SomeView: View {
    @EnvironmentObject var eo: SomeEO
    @ObservedObject var vm: SomeVM

    init(vm: SomeVM) {
       self.vm = vm
    }

    var body: some View {
        // environment object will be injected automatically if declared inside ChildView
        ChildView(vm: ChildVM(eo: self.eo)) 
    }
}

struct ChildView: View {
    @EnvironmentObject var eo: SomeEO
    @ObservedObject var vm: ChildVM

    init(vm: ChildVM) {
       self.vm = vm
    }

    var body: some View {
        Text("Just demo stub")
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.