UIViewControllers और उनके बीच नेविगेशन की संरचना (और न केवल)


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


https://github.com/ekazaev/route-composer


लेकिन, शुरुआत के लिए, आइए यह जानने की कोशिश करें कि आईओएस में व्यू कंट्रोलर्स की संरचना का क्या मतलब है।


स्पष्टीकरण के लिए आगे बढ़ने से पहले, मैं आपको याद दिलाता हूं कि iOS में इसे सबसे अधिक बार एक नियंत्रक या UIViewController रूप में समझा जाता है। यह मानक UIViewController से विरासत में मिला एक वर्ग है, जो आधार MVC पैटर्न नियंत्रक है जिसे Apple iOS अनुप्रयोगों के लिए उपयोग करने की सिफारिश करता है।


आप MVVM, VIP, VIPER जैसे वैकल्पिक वास्तुशिल्प पैटर्न का उपयोग कर सकते हैं, लेकिन उनमें UIViewController एक या दूसरे तरीके से शामिल होगा, जिसका अर्थ है कि इस पुस्तकालय का उपयोग उनके साथ किया जा सकता है। UIViewController सार का उपयोग UIView को नियंत्रित करने के लिए किया जाता है, जो अक्सर एक स्क्रीन या स्क्रीन के एक महत्वपूर्ण हिस्से का प्रतिनिधित्व करता है, इससे घटनाओं को संसाधित करता है और इसमें कुछ डेटा प्रदर्शित करता है।



सभी UIViewController को सशर्त रूप से Normal View नियंत्रकों में विभाजित किया जा सकता है, जो स्क्रीन पर कुछ दृश्य क्षेत्र के लिए ज़िम्मेदार हैं, और कंटेनर दृश्य नियंत्रक , जो स्वयं को और उनके कुछ नियंत्रणों को प्रदर्शित करने के अलावा, एक या दूसरे तरीके से उनमें एकीकृत किए गए व्यू कंट्रोलर भी प्रदर्शित कर सकते हैं। ।


कोको टच के साथ आपूर्ति किए जाने वाले मानक कंटेनर दृश्य नियंत्रकों में शामिल हैं: UINavigationConroller , UITabBarController , UISplitController , UIPageController और कुछ अन्य। इसके अलावा, उपयोगकर्ता Apple प्रलेखन में वर्णित कोको टच नियमों का पालन करके अपने स्वयं के कस्टम कंटेनर व्यू कंट्रोलर बना सकते हैं।


कंटेनर दृश्य नियंत्रकों में मानक दृश्य नियंत्रकों को पेश करने की प्रक्रिया, साथ ही साथ नियंत्रकों में दृश्य नियंत्रकों के एकीकरण को स्टैक किया जाता है, हम इस लेख में रचना को कॉल करेंगे।


फिर, व्यू कंट्रोलर्स की रचना के लिए मानक समाधान हमारे लिए इष्टतम नहीं था, और हमने एक पुस्तकालय विकसित किया जो हमारे काम को सुगम बनाता है।


आइए एक उदाहरण के रूप में कुछ मानक कंटेनर दृश्य नियंत्रकों की संरचना पर एक नज़र डालें:


मानक कंटेनरों में रचना के उदाहरण


UINavigationController



 let tableViewController = UITableViewController(style: .plain) //        let navigationController = UINavigationController(rootViewController: tableViewController) // ... //        let detailViewController = UIViewController(nibName: "DetailViewController", bundle: nil) navigationController.pushViewController(detailViewController, animated: true) // ... //     navigationController.popToRootViewController(animated: true) 

UITabBarController



 let firstViewController = UITableViewController(style: .plain) let secondViewController = UIViewController() //   let tabBarController = UITabBarController() //         tabBarController.viewControllers = [firstViewController, secondViewController] //        tabBarController.selectedViewController = secondViewController 

UISplitViewController



 let firstViewController = UITableViewController(style: .plain) let secondViewController = UIViewController() //   let splitViewController = UISplitViewController() //        splitViewController.viewControllers = [firstViewController] //        splitViewController.showDetailViewController(secondViewController, sender: nil) 

स्टैक पर दृश्य नियंत्रकों के एकीकरण (रचना) के उदाहरण


कंट्रोलर रूट को इंस्टॉल करना


 let window: UIWindow = //... window.rootViewController = viewController window.makeKeyAndVisible() 

दृश्य नियंत्रक की मोडल प्रस्तुति


 window.rootViewController.present(splitViewController, animated: animated, completion: nil) 

हमने रचना के लिए एक पुस्तकालय बनाने का फैसला क्यों किया


जैसा कि आप ऊपर दिए गए उदाहरणों से देख सकते हैं, पारंपरिक दृश्य नियंत्रकों को कंटेनरों में एकीकृत करने का कोई एक ही तरीका नहीं है, जैसे कि दृश्य नियंत्रकों के ढेर का निर्माण करने का कोई एक तरीका नहीं है। और, यदि आप अपने एप्लिकेशन के लेआउट या उसमें नेविगेट करने के तरीके को थोड़ा बदलना चाहते हैं, तो आपको एप्लिकेशन कोड में महत्वपूर्ण बदलावों की आवश्यकता होगी, आपको कंटेनर ऑब्जेक्ट्स के लिंक की भी आवश्यकता होगी, ताकि आप अपने व्यू कंट्रोलर्स को उनमें डाल सकें, आदि। यही है, मानक विधि का तात्पर्य काम की एक बड़ी मात्रा से है, साथ ही साथ अन्य नियंत्रकों के कार्यों और प्रस्तुतियों के लिए नियंत्रकों को देखने के लिए लिंक की उपस्थिति है।


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


केवल goToAccount() , goToMenu() या goToProduct(withId: "012345") जैसे तरीकों को कॉल करना बहुत अच्छा होगा जब कोई उपयोगकर्ता किसी बटन पर क्लिक करता है या जब कोई एप्लिकेशन किसी अन्य एप्लिकेशन से goToProduct(withId: "012345") सार्वभौमिक लिंक goToProduct(withId: "012345") और स्टैक पर इस दृश्य नियंत्रक को एकीकृत करने के बारे में नहीं सोचता है, तो स्टैक पर जानकर इस दृश्य नियंत्रक के निर्माता ने इस कार्यान्वयन को पहले ही प्रदान कर दिया है।


इसके अलावा, अक्सर, हमारे अनुप्रयोगों में विभिन्न टीमों द्वारा विकसित स्क्रीन की एक बड़ी संख्या होती है, और विकास प्रक्रिया के दौरान स्क्रीन में से एक को पाने के लिए, आपको एक और स्क्रीन से गुजरने की आवश्यकता होती है जो अभी तक नहीं बनाई जा सकती है। हमारी कंपनी में, हमने उस दृष्टिकोण का उपयोग किया जिसे हम पेट्री डिश कहते हैं । यही है, विकास मोड में, डेवलपर और परीक्षक के पास सभी एप्लिकेशन स्क्रीन की सूची तक पहुंच होती है और वह उनमें से किसी पर भी जा सकता है (बेशक, उनमें से कुछ को कुछ इनपुट मापदंडों की आवश्यकता हो सकती है)।



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


यह जोड़ना बाकी है कि यह सब एन द्वारा गुणा किया जाएगा जैसे ही आपकी मार्केटिंग टीम लाइव उपयोगकर्ताओं पर ए / बी परीक्षण करने की इच्छा व्यक्त करती है और जांचती है कि कौन सी नेविगेशन विधि बेहतर काम करती है, उदाहरण के लिए, टैब बार या हैमबर्गर मेनू?


  • चलो सुसानिन के पैर काट दिए ताब बार के उपयोगकर्ताओं के 50%, और अन्य हैमबर्गर मेनू को दिखाते हैं, और एक महीने में हम आपको बताएंगे कि कौन से उपयोगकर्ता हमारे विशेष प्रस्तावों को देखते हैं?

मैं आपको यह बताने की कोशिश करूंगा कि हमने इस समस्या के समाधान के लिए कैसे संपर्क किया और आखिरकार इसे रूटकॉमपॉपर लाइब्रेरी को आवंटित कर दिया।


Susanin मार्ग संगीतकार


रचना और नेविगेशन के सभी परिदृश्यों का विश्लेषण करने के बाद, हमने ऊपर दिए गए उदाहरणों में दिए गए कोड को अमूर्त करने की कोशिश की और 3 मुख्य संस्थाओं की पहचान की, जिनमें से रूटकॉमर्स लाइब्रेरी का संचालन होता है - Factory , Finder , Action । इसके अलावा, लाइब्रेरी में 3 सहायक RoutingInterceptor होती हैं जो थोड़ी ट्यूनिंग के लिए ज़िम्मेदार होती हैं जो कि नेविगेशन प्रक्रिया के दौरान आवश्यक हो सकती हैं - RoutingInterceptor , ContextTask , PostRoutingTask । इन सभी संस्थाओं को निर्भरता की श्रृंखला में कॉन्फ़िगर किया जाना चाहिए और Router वाई को स्थानांतरित किया जाना चाहिए, वह वस्तु जो आपके नियंत्रकों के ढेर का निर्माण करेगी।


लेकिन, उनमें से प्रत्येक के बारे में:


फ़ैक्टरी


जैसा कि नाम से पता चलता है, Factory व्यू कंट्रोलर बनाने के लिए जिम्मेदार है।


 public protocol Factory { associatedtype ViewController: UIViewController associatedtype Context func build(with context: Context) throws -> ViewController } 

यहां संदर्भ की अवधारणा के बारे में आरक्षण करना महत्वपूर्ण है। पुस्तकालय के भीतर संदर्भ, हम सब कुछ कहते हैं जिसे दर्शक को बनाने की आवश्यकता हो सकती है। उदाहरण के लिए, उत्पाद विवरण प्रदर्शित करने वाले एक दृश्य नियंत्रक को दिखाने के लिए, आपको एक निश्चित उत्पादआईडी को इसमें पारित करने की आवश्यकता है, उदाहरण के लिए, String । संदर्भ का सार कुछ भी हो सकता है: एक वस्तु, संरचना, ब्लॉक या टपल। यदि आपके नियंत्रक को बनाने के लिए किसी चीज की आवश्यकता नहीं है - क्या संदर्भ को Any? रूप में निर्दिष्ट किया जा सकता है Any? और nil में स्थापित करें।


उदाहरण के लिए:


 class ProductViewControllerFactory: Factory { func build(with productID: UUID) throws -> ProductViewController { let productViewController = ProductViewController(nibName: "ProductViewController", bundle: nil) productViewController.productID = productID //  ,      `ContextAction`,     return productViewController } } 

ऊपर दिए गए कार्यान्वयन से यह स्पष्ट हो जाता है कि यह कारखाना XIB फ़ाइल से नियंत्रक छवि को लोड करेगा और इसमें स्थानांतरित उत्पाद को स्थापित करेगा। मानक Factory प्रोटोकॉल के अलावा, लाइब्रेरी आपको इस प्रोटोकॉल के कई मानक कार्यान्वयन प्रदान करती है ताकि आप इसे बैंल कोड (विशेष रूप से, ऊपर का उदाहरण) लिखने से बचा सकें।


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


कार्य


Action इकाई एक विवरण नियंत्रक को एकीकृत करने के तरीके का विवरण है, जिसे कारखाने द्वारा, स्टैक पर बनाया जाएगा। निर्माण के बाद दृश्य नियंत्रक केवल हवा में लटका नहीं सकता है और इसलिए, प्रत्येक कारखाने में Action होना चाहिए जैसा कि ऊपर दिए गए उदाहरण से देखा जा सकता है।


Action का सबसे सामान्य कार्यान्वयन नियंत्रक की मोडल प्रस्तुति है:


 class PresentModally: Action { func perform(viewController: UIViewController, on existingController: UIViewController, animated: Bool, completion: @escaping (_: ActionResult) -> Void) { guard existingController.presentedViewController == nil else { completion(.failure("\(existingController) is already presenting a view controller.")) return } existingController.present(viewController, animated: animated, completion: { completion(.continueRouting) }) } } 

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


खोजक


Finder का सार प्रश्न के लिए राउटर का जवाब देता है - क्या ऐसा नियंत्रक पहले से ही बनाया गया है और क्या यह पहले से ही स्टैक पर है? शायद कुछ भी बनाने की आवश्यकता नहीं है और यह दिखाने के लिए पर्याप्त है कि पहले से क्या है?


 public protocol Finder { associatedtype ViewController: UIViewController associatedtype Context func findViewController(with context: Context) -> ViewController? } 

यदि आप अपने द्वारा बनाए गए सभी दृश्य नियंत्रकों के लिए लिंक संग्रहीत करते हैं, तो अपने Finder कार्यान्वयन में आप केवल वांछित दृश्य नियंत्रक के लिंक को वापस कर सकते हैं। लेकिन सबसे अधिक बार ऐसा नहीं होता है, क्योंकि एप्लिकेशन स्टैक, खासकर यदि यह बड़ा है, तो काफी गतिशील रूप से बदलता है। इसके अलावा, आप विभिन्न संस्थाओं को दिखाते हुए स्टैक पर कई समान दृश्य नियंत्रक रख सकते हैं (उदाहरण के लिए, कई ProductViewControllers विभिन्न उत्पाद के साथ विभिन्न उत्पाद दिखा रहे हैं), इसलिए Finder कार्यान्वयन को स्टैक पर संबंधित दृश्य नियंत्रक के लिए कस्टम कार्यान्वयन और खोज की आवश्यकता हो सकती है। पुस्तकालय इस कार्य को सरल बनाने के लिए उपयुक्त सेटिंग्स के साथ एक प्रोटोकॉल, Finder विस्तार के रूप में StackIteratingFinder प्रदान करके इस कार्य की सुविधा प्रदान करता है। StackIteratingFinder के कार्यान्वयन में StackIteratingFinder आपको केवल प्रश्न का उत्तर देने की आवश्यकता है - क्या यह दृश्य नियंत्रक है जो राउटर आपके अनुरोध पर देख रहा है।


इस तरह के कार्यान्वयन का एक उदाहरण:


 class ProductViewControllerFinder: StackIteratingFinder { let options: SearchOptions init(options: SearchOptions = .currentAndUp) { self.options = options } func isTarget(_ productViewController: ProductViewController, with productID: UUID) -> Bool { return productViewController.productID == productID } } 

हेल्पर एंटिटीज


RoutingInterceptor


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


 class LoginInterceptor: RoutingInterceptor { func execute(for destination: AppDestination, completion: @escaping (_: InterceptorResult) -> Void) { guard !LoginManager.sharedInstance.isUserLoggedIn else { // ... //  LoginViewController       completion(.success)  completion(.failure("User has not been logged in.")) // ... return } completion(.success) } } 

टिप्पणियों के साथ इस तरह के RoutingInterceptor का कार्यान्वयन पुस्तकालय के साथ आपूर्ति किए गए उदाहरण में निहित है।


ContextTask


ContextTask , यदि आप इसे प्रदान करते हैं, तो कॉन्फ़िगरेशन में प्रत्येक दृश्य नियंत्रक के लिए अलग से लागू किया जा सकता है, भले ही यह सिर्फ एक राउटर द्वारा बनाया गया था या स्टैक पर पाया गया था, और आप बस इसमें डेटा अपडेट करना चाहते हैं या अन्य डिफ़ॉल्ट सेट करना चाहते हैं पैरामीटर (उदाहरण के लिए, नज़दीकी बटन दिखाएं या न दिखाएं)।


PostRoutingTask


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


सभी वर्णित संस्थाओं के कार्यान्वयन के साथ अधिक विवरण में पुस्तकालय के लिए प्रलेखन में और साथ ही संलग्न उदाहरण में पाया जा सकता है।


पुनश्च: सहायक संस्थाओं की संख्या जो कॉन्फ़िगरेशन में जोड़ी जा सकती है, सीमित नहीं है।


विन्यास


सभी वर्णित निकाय अच्छे हैं कि वे रचना प्रक्रिया को छोटे, विनिमेय और अच्छी तरह से विश्वसनीय ब्लॉकों में तोड़ देते हैं।


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


एक तुच्छ उदाहरण पर विचार करें: मान लीजिए, सूची में एक सेल पर क्लिक करके या जब एप्लिकेशन को एक सफारी या ईमेल क्लाइंट से एक सार्वभौमिक लिंक प्राप्त होता है, तो हमें एक निश्चित उत्पादआईडी के साथ मामूली रूप से उत्पाद नियंत्रक को दिखाना होगा। इस स्थिति में, उत्पाद नियंत्रक को UINavigationController अंदर बनाया जाना चाहिए ताकि वह अपने नियंत्रण कक्ष पर अपना नाम और करीबी बटन दिखा सके। इसके अलावा, यह उत्पाद केवल उन उपयोगकर्ताओं को दिखाया जा सकता है जो लॉग इन हैं, अन्यथा, उन्हें लॉग इन करने के लिए आमंत्रित करें।


यदि आप लाइब्रेरी का उपयोग किए बिना इस उदाहरण को पार्स करते हैं, तो यह कुछ इस तरह दिखाई देगा:


 class ProductArrayViewController: UITableViewController { let products: [UUID]? let analyticsManager = AnalyticsManager.sharedInstance //  UITableViewControllerDelegate override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard let productID = products[indexPath.row] else { return } //   LoginInterceptor guard !LoginManager.sharedInstance.isUserLoggedIn else { //    LoginViewController         `showProduct(with: productID)` return } showProduct(with: productID) } func showProduct(with productID: String) { //   ProductViewControllerFactory let productViewController = ProductViewController(nibName: "ProductViewController", bundle: nil) //   ProductViewControllerContextTask productViewController.productID = productID //   NavigationControllerStep  PushToNavigationAction let navigationController = UINavigationController(rootViewController: productViewController) //   GenericActions.PresentModally present(alertController, animated: navigationController) { [weak self]   . ProductViewControllerPostTask self?.analyticsManager.trackProductView(productID: productID) } } } 

इस उदाहरण में सार्वभौमिक लिंक का कार्यान्वयन शामिल नहीं है, जिसे प्राधिकरण कोड को अलग करने और उस संदर्भ को बनाए रखने की आवश्यकता होगी जहां उपयोगकर्ता को निर्देशित किया जाना चाहिए, साथ ही खोज, अचानक उपयोगकर्ता एक लिंक पर क्लिक करता है, और यह उत्पाद पहले से ही उसे दिखाया गया है, जो अंततः कोड को बहुत अधिक बना देगा। पढ़ने में कठिन।


पुस्तकालय का उपयोग करके इस उदाहरण के विन्यास पर विचार करें:


 let productScreen = StepAssembly(finder: ProductViewControllerFinder(), factory: ProductViewControllerFactory()) //  : .adding(LoginInterceptor()) .adding(ProductViewControllerContextTask()) .adding(ProductViewControllerPostTask(analyticsManager: AnalyticsManager.sharedInstance)) //  : .using(PushToNavigationAction()) .from(NavigationControllerStep()) // NavigationControllerStep -> StepAssembly(finder: NilFinder(), factory: NavigationControllerFactory()) .using(GeneralAction.presentModally()) .from(GeneralStep.current()) .assemble() 

यदि आप इसका मानव भाषा में अनुवाद करते हैं:


  • जांचें कि उपयोगकर्ता लॉग इन है, और यदि उसे इनपुट नहीं दिया गया है
  • यदि उपयोगकर्ता सफलतापूर्वक लॉग इन कर चुका है, तो जारी रखें
  • खोज उत्पाद दृश्य नियंत्रक Finder द्वारा प्रदान किया गया
  • यदि यह पाया गया - दिखाई और खत्म करें
  • अगर यह नहीं पाया गया - एक UINavigationController बनाएं, इसमें ProductViewControllerFactory द्वारा बनाया गया व्यू कंट्रोलर एकीकृत करें जो PushToNavigationAction का उपयोग कर PushToNavigationAction
  • वर्तमान दृश्य नियंत्रक से GenericActions.PresentModally का उपयोग करके GenericActions.PresentModally UINavigationController GenericActions.PresentModally

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


एक पूर्ण अनुप्रयोग के छद्म कोड पर विचार करें जिसमें एक ProductArrayViewController उत्पादों की एक सूची प्रदर्शित करता है और, यदि उपयोगकर्ता इस उत्पाद का चयन करता है, तो यह इस पर निर्भर करता है कि उपयोगकर्ता लॉग इन है या नहीं, या एक सफल लॉगिन के बाद लॉग इन करने और प्रदर्शित करने की पेशकश करता है:


कॉन्फ़िगरेशन ऑब्जेक्ट्स


 // `RoutingDestination`    .          . struct AppDestination: RoutingDestination { let finalStep: RoutingStep let context: Any? } struct Configuration { //     ,             static func productDestination(with productID: UUID) -> AppDestination { let productScreen = StepAssembly(finder: ProductViewControllerFinder(), factory: ProductViewControllerFactory()) .add(LoginInterceptor()) .add(ProductViewControllerContextTask()) .add(ProductViewControllerPostTask(analyticsManager: AnalyticsManager.sharedInstance)) .using(PushToNavigationAction()) .from(NavigationControllerStep()) .using(GenericActions.PresentModally()) .from(CurrentControllerStep()) .assemble() return AppDestination(finalStep: productScreen, context: productID) } } 


 class ProductArrayViewController: UITableViewController { let products: [UUID]? //... // DefaultRouter -  Router   ,   UIViewController   let router = DefaultRouter() override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard let productID = products[indexPath.row] else { return } router.navigate(to: Configuration.productDestination(with: productID)) } } 


 @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { //... func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool { guard let productID = UniversalLinksManager.parse(url: url) else { return false } return DefaultRouter().navigate(to: Configuration.productDestination(with: productID)) == .handled } } 

.


. , , , — ProductArrayViewController, UINavigationController HomeViewController — StepAssembly from() . RouteComposer , ( ). , Configuration . , A/B , .


एक निष्कर्ष के बजाय


, 3 . , , . Fabric , Finder Action . , — , , . , .


, , objective c Cocoa Touch, . iOS 9 12.


UIViewController (MVC, MVVM, VIP, RIB, VIPER ..)


, , , . . .


.

Source: https://habr.com/ru/post/hi421097/


All Articles