IOS नेटवर्किंग एप्लिकेशन (REST क्लाइंट) के निर्माण के लिए सर्वश्रेष्ठ वास्तुशिल्प दृष्टिकोण


323

मैं कुछ अनुभव के साथ एक आईओएस डेवलपर हूं और यह प्रश्न मेरे लिए वास्तव में दिलचस्प है। मैंने इस विषय पर विभिन्न संसाधनों और सामग्रियों को देखा, लेकिन फिर भी मैं अभी भी भ्रमित हूं। IOS नेटवर्क अनुप्रयोग के लिए सबसे अच्छा आर्किटेक्चर क्या है? मेरा मतलब है मूल अमूर्त रूपरेखा, पैटर्न, जो हर नेटवर्किंग एप्लिकेशन को फिट करेगा चाहे वह एक छोटा ऐप हो जिसमें केवल कुछ सर्वर अनुरोध या एक जटिल REST क्लाइंट हो। Apple MVCसभी iOS अनुप्रयोगों के लिए एक बुनियादी वास्तुशिल्प दृष्टिकोण के रूप में उपयोग करने की सलाह देता है , लेकिन न MVCतो अधिक आधुनिकMVVM पैटर्न बताते हैं कि नेटवर्क लॉजिक कोड कहां रखा जाए और इसे सामान्य रूप से कैसे व्यवस्थित किया जाए।

क्या मुझे MVCS( जैसे ) के Sलिए कुछ विकसित करने की आवश्यकता है Serviceऔर इस Serviceपरत में सभी APIअनुरोधों और अन्य नेटवर्किंग तर्क हैं, जो परिप्रेक्ष्य में वास्तव में जटिल हो सकते हैं? कुछ शोध करने के बाद मुझे इसके लिए दो बुनियादी दृष्टिकोण मिले। यहां वेब-सेवा API(जैसे LoginRequestवर्ग या PostCommentRequestवर्ग और इसी तरह) के लिए हर नेटवर्क अनुरोध के लिए एक अलग वर्ग बनाने की सिफारिश की गई थी, जो सभी आधार अनुरोध सार वर्ग से विरासत में मिला है AbstractBaseRequestऔर इसके अलावा कुछ वैश्विक नेटवर्क प्रबंधक बनाने के लिए जो सामान्य नेटवर्किंग कोड को एन्क्रिप्ट करता है और अन्य प्राथमिकताएँ (यह AFNetworkingअनुकूलन या हो सकती हैRestKit ट्यूनिंग , अगर हमारे पास जटिल वस्तु मैपिंग और दृढ़ता है, या यहां तक ​​कि एक मानक एपीआई के साथ अपने नेटवर्क संचार कार्यान्वयन)। लेकिन यह दृष्टिकोण मेरे लिए एक उपरि लगता है। एक अन्य दृष्टिकोण में कुछ सिंगलटन APIडिस्पैचर या प्रबंधक वर्ग है जैसा कि पहले दृष्टिकोण में है, लेकिन नहीं की तरह इस प्रबंधक वर्ग का एक उदाहरण सार्वजनिक विधि के रूप में हर अनुरोध को संपुटित के लिए हर अनुरोध के लिए कक्षाएं बनाने के लिए और बदले: fetchContacts, loginUserतरीकों, आदि तो, सबसे अच्छा और सही तरीका क्या है? क्या अन्य दिलचस्प दृष्टिकोण हैं जो मुझे अभी तक नहीं पता हैं?

और क्या मुझे इस सभी नेटवर्किंग सामानों के लिए एक और परत बनानी चाहिए Service, या NetworkProviderपरत या जो कुछ भी मेरे MVCआर्किटेक्चर के ऊपर है , या यह परत मौजूदा MVCपरतों में एकीकृत (इंजेक्ट) होनी चाहिए जैसे Model?

मुझे पता है कि सुंदर दृष्टिकोण मौजूद है, या फिर फेसबुक क्लाइंट या लिंक्डइन जैसे मोबाइल राक्षस कैसे नेटवर्किंग तर्क की तेजी से बढ़ती जटिलता के साथ सौदा करते हैं?

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


14
क्या यह "खरीदारी की सूची" का सवाल नहीं है? मेरा बस एक सवाल था कि नरक में जाने के लिए वोट दिया गया था और बंद कर दिया गया था क्योंकि यह कहा गया था कि "क्या सबसे अच्छा है" प्रकार के प्रश्न बहुत अधिक असहनीय बहस को चिंगारी देते हैं। यह खरीदारी सूची प्रश्न को उत्थान और एक इनाम के योग्य प्रश्न बनाती है जबकि अन्य बंद हो जाते हैं?
एल्विन थॉम्पसन

1
आमतौर पर नेटवर्क तर्क नियंत्रक में जाता है, जो एक मॉडल ऑब्जेक्ट को बदल देगा और किसी भी प्रतिनिधि या पर्यवेक्षकों को सूचित करेगा।
प्रकाशित करें

1
बहुत ही दिलचस्प सवाल और जवाब। IOS कोडिंग के 4 साल बाद, और ऐप में नेटवर्क लेयर जोड़ने का सबसे सुंदर तरीका खोजने की कोशिश कर रहा है। नेटवर्क अनुरोध का प्रबंधन करने के लिए किस वर्ग की जिम्मेदारी होनी चाहिए? नीचे दिए गए उत्तर वास्तव में प्रासंगिक हैं। धन्यवाद
darksider

@JoeBlow यह सच नहीं है। मोबाइल ऐप उद्योग अभी भी सर्वर-क्लाइंट संचार पर बहुत निर्भर करता है।
scord

जवाबों:


327

I want to understand basic, abstract and correct architectural approach for networking applications in iOS: एप्लिकेशन आर्किटेक्चर के निर्माण के लिए कोई "सर्वश्रेष्ठ" या "सबसे सही" दृष्टिकोण नहीं है। यह एक बहुत ही रचनात्मक काम है। आपको हमेशा सबसे सीधा और एक्स्टेंसिबल आर्किटेक्चर चुनना चाहिए, जो किसी भी डेवलपर के लिए स्पष्ट होगा, जो आपकी परियोजना पर या आपकी टीम में अन्य डेवलपर्स के लिए काम करना शुरू कर देगा, लेकिन मैं मानता हूं, कि "अच्छा" और "बुरा" हो सकता है " आर्किटेक्चर।

आपने कहा: collect the most interesting approaches from experienced iOS developersमुझे नहीं लगता कि मेरा दृष्टिकोण सबसे दिलचस्प या सही है, लेकिन मैंने इसे कई परियोजनाओं में इस्तेमाल किया है और इससे संतुष्ट हूं। यह उन लोगों का एक संकर दृष्टिकोण है, जिनका आपने ऊपर उल्लेख किया है, और मेरे अपने शोध प्रयासों से सुधार के साथ भी। मैं इमारत के दृष्टिकोण की समस्याओं में दिलचस्प हूं, जो कई प्रसिद्ध पैटर्न और मुहावरों को जोड़ती है। मुझे लगता है कि बहुत से फाउलर के एंटरप्राइज पैटर्न को मोबाइल एप्लिकेशन पर सफलतापूर्वक लागू किया जा सकता है। यहां सबसे दिलचस्प लोगों की एक सूची है, जिसे हम एक iOS एप्लिकेशन आर्किटेक्चर ( मेरी राय में ) बनाने के लिए आवेदन कर सकते हैं : सेवा परत , कार्य की इकाई , दूरस्थ मुखौटा , डेटा ट्रांसफर ऑब्जेक्ट , गेटवे , लेयर सुपरटाइप , विशेष मामला , डोमेन मॉडल । आपको हमेशा एक मॉडल परत को सही ढंग से डिज़ाइन करना चाहिए और हमेशा दृढ़ता के बारे में मत भूलना (यह आपके ऐप के प्रदर्शन को काफी बढ़ा सकता है)। आप इसके लिए उपयोग कर सकते हैं Core Data। लेकिन आपको यह नहीं भूलना चाहिए , कि Core Dataयह ओआरएम या डेटाबेस नहीं है, बल्कि इसके अच्छे विकल्प के रूप में दृढ़ता के साथ एक वस्तु ग्राफ प्रबंधक है। इसलिए, बहुत बार Core Dataआपकी आवश्यकताओं के लिए बहुत भारी हो सकता है और आप नए समाधानों को देख सकते हैं जैसे कि Realm और Couchbase Lite , या कच्चे SQLite या LevelDB के आधार पर अपनी खुद की हल्की वस्तु मैपिंग / दृढ़ता परत का निर्माण करें।। इसके अलावा, मैं आपको सलाह देता हूं कि आप स्वयं को डोमेन संचालित डिजाइन और CQRS से परिचित कराएं

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

It encapsulates the application's business logic,  controlling transactions 
and coordinating responses in the implementation of its operations.

हमारे MVCक्षेत्र Service Layerमें डोमेन मॉडल और नियंत्रकों के बीच मध्यस्थ की तरह कुछ है। MVCS नामक इस दृष्टिकोण की एक समान भिन्नता है जहाँ Storeवास्तव में हमारी Serviceपरत है। Storeमॉडल उदाहरणों को प्रस्तुत करता है और नेटवर्किंग, कैशिंग आदि को संभालता है। मैं यह उल्लेख करना चाहता हूं कि आपको अपनी सेवा परत में अपने सभी नेटवर्किंग और व्यावसायिक तर्क नहीं लिखना चाहिए । यह भी एक बुरा डिजाइन माना जा सकता है। अधिक जानकारी के लिए एनीमिक और रिच डोमेन मॉडल देखें। कुछ सेवा विधियों और व्यावसायिक तर्क को मॉडल में संभाला जा सकता है, इसलिए यह एक "समृद्ध" (व्यवहार के साथ) मॉडल होगा।

मैं हमेशा बड़े पैमाने पर दो पुस्तकालयों का उपयोग करता हूं: AFNetworking 2.0 और ReactiveCocoa । मुझे लगता है कि यह किसी भी आधुनिक अनुप्रयोग के लिए होना चाहिए जो नेटवर्क और वेब-सेवाओं के साथ बातचीत करता है या इसमें जटिल यूआई लॉजिक होता है।

आर्किटेक्चर

सबसे पहले मैं एक सामान्य APIClientवर्ग बनाता हूं , जो AFHTTPSessionManager का उपवर्ग है । यह एप्लिकेशन में सभी नेटवर्किंग का एक वर्कहॉर्स है: सभी सेवा वर्ग इसे वास्तविक REST अनुरोध सौंपते हैं। इसमें HTTP क्लाइंट के सभी अनुकूलन शामिल हैं, जिनकी मुझे विशेष एप्लिकेशन में आवश्यकता है: SSL पिनिंग, त्रुटि प्रसंस्करण और NSErrorविस्तृत विफलता के कारणों के साथ सीधी वस्तुएं बनाना और सभी APIऔर कनेक्शन त्रुटियों का वर्णन (ऐसे मामले में नियंत्रक के लिए सही संदेश दिखा सकेंगे) उपयोगकर्ता), अनुरोध और प्रतिक्रिया क्रमबद्धक, http हेडर और अन्य नेटवर्क से संबंधित सामान सेट करना। तब मैं तार्किक subservices या, और अधिक सही ढंग से, में सभी API अनुरोधों को विभाजित microservices : UserSerivces, CommonServices,SecurityServices ,FriendsServicesऔर इसी तरह, व्यावसायिक तर्क के अनुसार वे लागू होते हैं। इनमें से प्रत्येक माइक्रोसर्विस एक अलग वर्ग है। वे, एक साथ, एक रूप Service Layer। इन कक्षाओं में प्रत्येक एपीआई अनुरोध, डोमेन मॉडल को संसाधित करने और हमेशा RACSignalपार्स किए गए प्रतिक्रिया मॉडल या NSErrorकॉलर के साथ रिटर्न के तरीके होते हैं ।

मैं यह उल्लेख करना चाहता हूं कि यदि आपके पास जटिल मॉडल क्रमांकन तर्क है - तो इसके लिए एक और परत बनाएं: डेटा मैपर जैसा कुछ लेकिन सामान्य रूप से JSON / XML -> मॉडल मैपर। यदि आपके पास कैश है: तो इसे एक अलग परत / सेवा के रूप में भी बनाएं (आपको कैशिंग के साथ व्यावसायिक तर्क नहीं मिलाना चाहिए)। क्यों? क्योंकि सही कैशिंग परत अपने स्वयं के गोच के साथ काफी जटिल हो सकती है। लोग जटिल तर्क को लागू करने के लिए वैध, पूर्वानुमानित कैशिंग जैसे उदाहरण पेश करते हैं, जैसे कि प्रोफेसरों पर आधारित अनुमानों के साथ मोनोडल कैशिंग। आप अधिक समझने के लिए कार्लोस नामक इस खूबसूरत पुस्तकालय के बारे में पढ़ सकते हैं । और यह मत भूलो कि कोर डेटा वास्तव में सभी कैशिंग मुद्दों के साथ आपकी मदद कर सकता है और आपको कम तर्क लिखने की अनुमति देगा। इसके अलावा, यदि आपके पास रिपॉजिटरी के बीच कुछ तर्क हैंNSManagedObjectContext मॉडल के और सर्वर अनुरोधों के हैं, तो आप उपयोग कर सकते हैं पैटर्न, जो डेटा को पुनर्प्राप्त करने वाले तर्क को अलग करता है और इसे मॉडल पर कार्य करने वाले व्यावसायिक तर्क से इकाई मॉडल में मैप करता है। इसलिए, मैं कोर डेटा आधारित आर्किटेक्चर होने पर भी रिपॉजिटरी पैटर्न का उपयोग करने की सलाह देता हूं। भंडार कर सकते हैं अमूर्त बातें, जैसे NSFetchRequest, NSEntityDescription, NSPredicateऔर इतने की तरह सादा तरीकों की पर getया put

सेवा परत में इन सभी कार्यों के बाद, कॉलर (व्यू कंट्रोलर) प्रतिक्रिया के साथ कुछ जटिल अतुल्यकालिक सामान कर सकता है: सिग्नल हेरफेर, चेनिंग, मैपिंग, ReactiveCocoaआदिम की मदद से , या बस इसे सब्सक्राइब करें और दृश्य में परिणाम दिखाएं। । मैं के साथ इंजेक्षन निर्भरता इंजेक्शन इन सभी सेवा कक्षाएं मेरे में APIClientहै, जो इसी में एक विशेष सेवा कॉल अनुवाद करेगा GET, POST, PUT, DELETE, आदि REST एंडपॉइंट के लिए अनुरोध। इस मामले APIClientमें सभी नियंत्रकों को स्पष्ट रूप से पारित कर दिया जाता है, आप इसे एक पैरामीरिज्ड ओवर के साथ स्पष्ट कर सकते हैंAPIClient सेवा वर्गों । यह समझ में आ सकता है कि क्या आप अलग-अलग कस्टमाइज़ेशन का उपयोग करना चाहते हैंAPIClientविशेष सेवा वर्गों के लिए, लेकिन यदि आप, कुछ कारणों से, अतिरिक्त प्रतियां नहीं चाहते हैं या आप सुनिश्चित हैं कि आप हमेशा एक विशेष उदाहरण (कस्टमाइज़ेशन के बिना) का उपयोग करेंगे APIClient- तो इसे एक सिंगलटन बनाएं, लेकिन डॉन, कृपया नहीं करें 'टी सर्विस क्लास को सिंगलटन बनाते हैं।

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

  1. एकल उदाहरण का स्वामित्व यथोचित असाइन नहीं किया जा सकता है;
  2. आलसी आरंभीकरण वांछनीय है;
  3. अन्यथा के लिए वैश्विक पहुंच प्रदान नहीं की गई है।

हमारे मामले में एकल उदाहरण का स्वामित्व कोई समस्या नहीं है और साथ ही हमें अपने वैश्विक प्रबंधक को सेवाओं में विभाजित करने के बाद वैश्विक पहुँच की आवश्यकता नहीं है, क्योंकि अब केवल एक या कई समर्पित नियंत्रकों को एक विशेष सेवा की आवश्यकता होती है (जैसे UserProfileनियंत्रक की आवश्यकता UserServicesऔर इतने पर) ।

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

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

यहाँ उदाहरण के लिए मेरी वास्तुकला का एक सामान्य वर्कफ़्लो है। मान लें कि हमारे पास एक है FriendsViewController, जो उपयोगकर्ता के दोस्तों की सूची प्रदर्शित करता है और हमारे पास दोस्तों को हटाने का एक विकल्प है। मैं अपनी FriendsServicesकक्षा में एक विधि बनाता हूँ जिसे:

- (RACSignal *)removeFriend:(Friend * const)friend

जहां Friendएक मॉडल / डोमेन ऑब्जेक्ट है (या यह केवल एक Userऑब्जेक्ट हो सकता है यदि उनके पास समान विशेषताएं हैं)। Underhood इस विधि को पार्स Friendकरने के लिए NSDictionaryJSON मापदंडों का friend_id, name, surname, friend_request_idऔर इतने पर। मैं हमेशा इस तरह के बॉयलरप्लेट के लिए और अपने मॉडल लेयर के लिए मेंटल लाइब्रेरी का उपयोग करता हूं (आगे और पीछे पार्स करना, JSON में नेस्टेड ऑब्जेक्ट पदानुक्रमों को प्रबंधित करना)। पार्स करने के बाद यह कॉल APIClient DELETEएक वास्तविक बाकी अनुरोध और रिटर्न बनाने के लिए विधि Responseमें RACSignalफोन करने वाले (करने के लिए FriendsViewControllerहमारे मामले में) उपयोगकर्ता या जो कुछ भी के लिए उचित संदेश प्रदर्शित करने के लिए।

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

एक वैचारिक सेट के रूप में एक रिपॉजिटरी एक निश्चित प्रकार की सभी वस्तुओं का प्रतिनिधित्व करता है। यह एक संग्रह की तरह काम करता है, और अधिक विस्तृत क्वेरी क्षमता को छोड़कर।

इसलिए, Repositoryअनिवार्य रूप से एक मुखौटा है जो डेटा / वस्तुओं तक पहुंच की आपूर्ति करने के लिए संग्रह शैली शब्दार्थ (ऐड, अपडेट, निकालें) का उपयोग करता है। : कारण है कि जब आप की तरह कुछ है कि getFriendsList, getUserGroups, removeFriendआप इसे में जगह कर सकते हैं Repositoryक्योंकि अपने संग्रह की तरह अर्थ विज्ञान सुंदर को यहां साफ़ है। और कोड की तरह:

- (RACSignal *)approveFriendRequest:(FriendRequest * const)request;

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

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

इसलिए, मैंने अपने सामान्य वास्तुशिल्प दृष्टिकोण का वर्णन किया, जिसे किसी भी आवेदन के लिए अनुकूलित किया जा सकता है, मुझे लगता है। इसमें और भी बहुत कुछ सुधार हो सकते हैं। मैं आपको कार्यात्मक प्रोग्रामिंग सीखने की सलाह देता हूं, क्योंकि आप इससे बहुत लाभ उठा सकते हैं, लेकिन इसके साथ बहुत दूर न जाएं। अत्यधिक, साझा, वैश्विक परिवर्तनशील राज्य को समाप्त करने, एक अपरिवर्तनीय डोमेन मॉडल बनाने या बाहरी दुष्प्रभावों के बिना शुद्ध कार्य बनाने के लिए, आम तौर पर, एक अच्छा अभ्यास है, और नई Swiftभाषा इसे प्रोत्साहित करती है। लेकिन हमेशा याद रखें, भारी शुद्ध कार्यात्मक पैटर्न के साथ अपने कोड को ओवरलोड करना, श्रेणी-सैद्धांतिक दृष्टिकोण एक बुरा विचार है, क्योंकि आपके अपरिवर्तनीय मॉडल में अन्य डेवलपर्स आपके कोड को पढ़ेंगे और समर्थन करेंगे, और वे निराश या डरावना हो सकते हैंprismatic profunctors और इस तरह के सामान। के साथ एक ही बात ReactiveCocoa: RACifyअपने कोड नहीं है बहुत ज्यादा है , क्योंकि यह वास्तव में तेजी से अपठनीय बन सकता है, विशेष रूप से newbies के लिए। इसका उपयोग तब करें जब यह वास्तव में आपके लक्ष्यों और तर्क को सरल बना सके।

तो, read a lot, mix, experiment, and try to pick up the best from different architectural approaches। यह सबसे अच्छी सलाह है जो मैं आपको दे सकता हूं।


इसके अलावा एक दिलचस्प और ठोस दृष्टिकोण। धन्यवाद।
मेनस्ट्रीमडेलवेपर00

1
@darksider जैसा कि मैंने पहले ही अपने उत्तर में लिखा है: "` मैं कभी भी एकल, ईश्वर APIManagerWhatever वर्ग या अन्य गलत सामान का उपयोग नहीं करता, क्योंकि सिंगलटन एक विरोधी पैटर्न है, और ज्यादातर मामलों में (दुर्लभ लोगों को छोड़कर) एक गलत समाधान है। ". I don't like singletons. I have an opinion that if you decided to use singletons in your project you should have at least three criteria why you do this (I edited my answer). So I inject them (lazy of course and not each time, but एक बार । `) प्रत्येक नियंत्रक में।
ओलेकेंडर काराबेरोव

14
हाय @alexander। क्या आपके पास GitHub पर कोई उदाहरण परियोजनाएं हैं? आप बहुत दिलचस्प दृष्टिकोण का वर्णन करते हैं। धन्यवाद। लेकिन मैं ऑब्जेक्टिव-सी डेवलपमेंट में एक शुरुआत हूं। और मेरे लिए कुछ पहलुओं को समझना मुश्किल है। शायद आप GitHub पर कुछ परीक्षण परियोजना अपलोड कर सकते हैं और एक लिंक दे सकते हैं?
डेनिस

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

1
@icodebuster यह डेमो प्रोजेक्ट मुझे यहाँ उल्लिखित कई अवधारणाओं को समझने में मदद कर रहा है: github.com/darthpelo/NetworkLayerExample

31

इस प्रश्न के लक्ष्य के अनुसार, हम अपने वास्तुकला दृष्टिकोण का वर्णन करना चाहते हैं।

वास्तुकला दृष्टिकोण

हमारे सामान्य iOS एप्लिकेशन का आर्किटेक्चर निम्नलिखित पैटर्न पर खड़ा है: सर्विस लेयर्स , MVVM , UI डेटा बाइंडिंग , डिपेंडेंसी इंजेक्शन ; और कार्यात्मक प्रतिक्रियाशील प्रोग्रामिंग प्रतिमान।

हम तार्किक परतों का पालन करने में एक विशिष्ट उपभोक्ता का सामना कर सकते हैं:

  • सभा
  • नमूना
  • सेवाएं
  • भंडारण
  • प्रबंधक
  • समन्वयकों
  • यूआई
  • भूमिकारूप व्यवस्था

असेंबली लेयर हमारे एप्लिकेशन का बूटस्ट्रैप बिंदु है। इसमें एक डिपेंडेंसी इंजेक्शन कंटेनर और एप्लिकेशन की वस्तुओं और उनकी निर्भरता की घोषणाएं शामिल हैं। इस परत में एप्लिकेशन का कॉन्फ़िगरेशन (urls, 3rd पार्टी सेवाएँ कुंजियाँ और इसी तरह) हो सकती हैं। इस उद्देश्य के लिए हम टाइफून का उपयोग करते हैं पुस्तकालय का ।

मॉडल परत में डोमेन मॉडल वर्ग, सत्यापन, मैपिंग शामिल हैं। हम अपने मॉडल की मैपिंग के लिए मेंटल लाइब्रेरी का उपयोग करते हैं: यह प्रारूपण JSONऔर NSManagedObjectमॉडल में क्रमांकन / डिसेरिएलाइज़ेशन का समर्थन करता है । हमारे मॉडल के सत्यापन और फॉर्म प्रतिनिधित्व के लिए हम FXForms और FXModelValidation पुस्तकालयों का उपयोग करते हैं।

सेवाओं की परत उन सेवाओं की घोषणा करती है जिनका उपयोग हम बाहरी सिस्टम के साथ बातचीत करने के लिए करते हैं ताकि हमारे डोमेन मॉडल में डेटा को भेजने या प्राप्त करने के लिए उपयोग किया जा सके। इसलिए आमतौर पर हमारे पास सर्वर APIs (प्रति इकाई), मैसेजिंग सर्विसेज (जैसे PubNub ), स्टोरेज सर्विसेज (जैसे Amazon S3), आदि के लिए संचार सेवाएं होती हैं। मूल रूप से SDK द्वारा प्रदान की जाने वाली वस्तुएं (उदाहरण के लिए PubNub SDK) और अपने स्वयं के संचार को लागू करती हैं। तर्क। सामान्य नेटवर्किंग के लिए हम AFNetworking लाइब्रेरी का उपयोग करते हैं ।

संग्रहण परत का उद्देश्य डिवाइस पर स्थानीय डेटा संग्रहण को व्यवस्थित करना है। हम इसके लिए Core Data या Realm का उपयोग करते हैं (दोनों के पेशेवरों और विपक्ष हैं, जो तय करना है कि क्या उपयोग करना है यह ठोस चश्मे पर आधारित है)। कोर डेटा सेटअप के लिए हम MDMCoreData पुस्तकालय और कक्षाओं का गुच्छा - स्टोरेज - (सेवाओं के समान) का उपयोग करते हैं जो हर इकाई के लिए स्थानीय भंडारण तक पहुंच प्रदान करते हैं। दायरे के लिए, हम स्थानीय भंडारण तक पहुंच के लिए इसी तरह के भंडारण का उपयोग करते हैं।

प्रबंधकों की परत एक ऐसी जगह है जहाँ हमारे अमूर्त / आवरण रहते हैं।

एक प्रबंधक भूमिका में हो सकता है:

  • क्रेडेंशियल्स प्रबंधक इसके विभिन्न कार्यान्वयनों के साथ (चाबी का गुच्छा, NSDefaults, ...)
  • वर्तमान सत्र प्रबंधक जो वर्तमान उपयोगकर्ता सत्र को रखना और प्रदान करना जानता है
  • मीडिया उपकरणों (वीडियो रिकॉर्डिंग, ऑडियो, चित्र लेने) तक पहुंच प्रदान करने वाली पाइपलाइन पर कब्जा
  • BLE प्रबंधक जो ब्लूटूथ सेवाओं और बाह्य उपकरणों तक पहुंच प्रदान करता है
  • जियो लोकेशन मैनेजर
  • ...

तो, प्रबंधक की भूमिका में कोई भी वस्तु हो सकती है जो किसी विशेष पहलू के तर्क को लागू करती है या आवेदन कार्य के लिए आवश्यक चिंता है।

हम सिंग्लेटन्स से बचने की कोशिश करते हैं, लेकिन यह परत एक ऐसी जगह है जहाँ वे ज़रूरत पड़ने पर रहते हैं।

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

  1. मान्य संदेश (मॉडल परत)
  2. संदेश स्थानीय रूप से सहेजें (संदेश संग्रहण)
  3. संदेश अनुलग्नक अपलोड करें (amazon s3 सेवा)
  4. संदेश की स्थिति और अनुलग्नकों को अपडेट करें और स्थानीय रूप से संदेश सहेजें (संदेश संग्रहण)
  5. संदेश को JSON प्रारूप (मॉडल परत) में क्रमबद्ध करें
  6. PubNub (PubNub सेवा) को संदेश प्रकाशित करें
  7. संदेश की स्थिति और विशेषताओं को अपडेट करें और इसे स्थानीय रूप से सहेजें (संदेश संग्रहण)

ऊपर दिए गए प्रत्येक चरण में एक त्रुटि इसी के अनुसार होती है।

UI परत में निम्नलिखित सबलेयर होते हैं:

  1. ViewModels
  2. ViewControllers
  3. दृश्य

बड़े पैमाने पर देखने वाले नियंत्रकों से बचने के लिए हम MVVM पैटर्न का उपयोग करते हैं और ViewModels में UI प्रस्तुति के लिए आवश्यक तर्क को लागू करते हैं। एक ViewModel में आमतौर पर समन्वयक और प्रबंधक निर्भरता के रूप में होते हैं। ViewMontels ViewControllers और दृश्य के कुछ प्रकार (जैसे टेबल व्यू सेल) द्वारा उपयोग किया जाता है। ViewControllers और ViewModels के बीच गोंद डेटा बाइंडिंग और कमांड पैटर्न है। आदेश में यह संभव है कि गोंद हम ReactiveCocoa का उपयोग करें लाइब्रेरी का ।

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

हम घोषणात्मक तरीके से अपने UI व्यवहार को लागू करने का प्रयास करते हैं। डेटा बाइंडिंग और ऑटो लेआउट इस लक्ष्य को प्राप्त करने में बहुत मदद करता है।

इन्फ्रास्ट्रक्चर लेयर में सभी हेल्पर्स, एक्सटेंशन, उपयोगिताओं के लिए आवेदन कार्य की आवश्यकता होती है।


यह दृष्टिकोण हमारे लिए और उन प्रकार के ऐप्स के लिए अच्छा है जो हम आमतौर पर बनाते हैं। लेकिन आपको समझना चाहिए, कि यह केवल एक व्यक्तिपरक दृष्टिकोण है जिसे ठोस टीम के उद्देश्य के लिए अनुकूलित / परिवर्तित किया जाना चाहिए

आशा है कि यह आपकी मदद करेगा!

इसके अलावा आप इस ब्लॉग पोस्ट में आईओएस डेवलपमेंट प्रोसेस के बारे में iOS डेवलपमेंट प्रोसेस के बारे में अधिक जानकारी पा सकते हैं


कुछ महीने पहले इस वास्तुकला को पसंद करना शुरू किया, इसे साझा करने के लिए एलेक्स को धन्यवाद! मैं निकट भविष्य में इसे RxSwift के साथ आज़माना चाहूंगा!
इंगहैम

18

क्योंकि सभी iOS ऐप अलग-अलग हैं, मुझे लगता है कि विचार करने के लिए यहां अलग-अलग दृष्टिकोण हैं, लेकिन मैं आमतौर पर इस तरह से जाता हूं:
सभी एपीआई अनुरोधों (आमतौर पर APICommunicator नाम) को संभालने के लिए एक केंद्रीय प्रबंधक (सिंगलटन) क्लास बनाएं और हर इंस्टेंस विधि एक एपीआई कॉल है । और एक केंद्रीय (गैर-सार्वजनिक) विधि है:

-(RACSignal *)sendGetToServerToSubPath:(NSString *)path withParameters:(NSDictionary *)params;

रिकॉर्ड के लिए, मैं 2 प्रमुख पुस्तकालयों / चौखटे, रिएक्टिवकोआ और एएफनेटवर्किंग का उपयोग करता हूं। ReactiveCocoa पूरी तरह से async नेटवर्किंग प्रतिक्रियाओं को संभालता है, आप (sendNext :, sendError :, आदि) कर सकते हैं।
यह विधि एपीआई को कॉल करती है, परिणाम प्राप्त करती है और उन्हें आरएसी के माध्यम से 'कच्चे' प्रारूप में भेजती है (जैसे NSArray क्या AFNetwork रिटर्न देता है)।
फिर एक विधि getStuffList:जिसे उपर्युक्त विधि कहा जाता है, वह संकेत है, कच्चे डेटा को ऑब्जेक्ट्स (मोती जैसी किसी चीज़ के साथ) में पार्स करती है और एक-एक करके ऑब्जेक्ट्स को कॉलर को भेजती है ( getStuffList:और इसी तरह के तरीकों से एक सिग्नल भी वापस आ जाता है जिसे कंट्रोलर सदस्यता ले सकता है )।
सब्स्क्राइब्ड कंट्रोलर ऑब्जेक्ट को subscribeNext:ब्लॉक करके प्राप्त करता है और उन्हें हैंडल करता है।

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


2
उत्तर के लिए धन्यवाद +1। अच्छी एप्रोच। मैं सवाल छोड़ता हूं। हो सकता है कि हमारे पास अन्य डेवलपर्स से कुछ अन्य दृष्टिकोण होंगे।
मेनस्ट्रीमड्यूपर 00

1
मुझे इस दृष्टिकोण में भिन्नता पसंद है - मैं एक केंद्रीय एपीआई प्रबंधक का उपयोग करता हूं जो एपीआई के साथ संवाद करने के यांत्रिकी का ख्याल रखता है। हालांकि मैं अपने मॉडल ऑब्जेक्ट्स पर सभी कार्यक्षमता को उजागर करने का प्रयास करता हूं। मॉडल जैसे तरीके प्रदान करेंगे + (void)getAllUsersWithSuccess:(void(^)(NSArray*))success failure:(void(^)(NSError*))failure;और - (void)postWithSuccess:(void(^)(instancetype))success failure:(void(^)(NSError*))failure;जो आवश्यक तैयारी करते हैं और फिर एपीआई प्रबंधक को कॉल करते हैं।
जेस्लर

1
यह दृष्टिकोण सीधा है, लेकिन चूंकि एपीआई की संख्या बढ़ रही है, इसलिए सिंगलटन एपीआई प्रबंधक को बनाए रखना कठिन हो जाता है। और हर नया जोड़ा एपीआई प्रबंधक से संबंधित होगा, कोई फर्क नहीं पड़ता कि यह एपीआई किस मॉड्यूल से संबंधित है। API अनुरोधों को प्रबंधित करने के लिए github.com/kevin0571/STNetTaskQueue का उपयोग करने का प्रयास करें ।
केविन

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

8

मेरी स्थिति में मैं आमतौर पर ResKit का उपयोग कर रहा हूं नेटवर्क लेयर को सेट करने के लिए लाइब्रेरी । यह उपयोग में आसान पार्सिंग प्रदान करता है। यह अलग-अलग प्रतिक्रियाओं और सामान के लिए मैपिंग स्थापित करने के मेरे प्रयास को कम करता है।

मैं केवल मैपिंग को स्वचालित रूप से सेटअप करने के लिए कुछ कोड जोड़ता हूं। मैं अपने मॉडल के लिए आधार वर्ग को परिभाषित करता हूं (कुछ विधि लागू होने या नहीं होने की जाँच करने के लिए कोड के बहुत अधिक होने के कारण प्रोटोकॉल नहीं, और स्वयं मॉडल में कम कोड है:

MappableEntry.h

@interface MappableEntity : NSObject

+ (NSArray*)pathPatterns;
+ (NSArray*)keyPathes;
+ (NSArray*)fieldsArrayForMapping;
+ (NSDictionary*)fieldsDictionaryForMapping;
+ (NSArray*)relationships;

@end

MappableEntry.m

@implementation MappableEntity

+(NSArray*)pathPatterns {
    return @[];
}

+(NSArray*)keyPathes {
    return nil;
}

+(NSArray*)fieldsArrayForMapping {
    return @[];
}

+(NSDictionary*)fieldsDictionaryForMapping {
    return @{};
}

+(NSArray*)relationships {
    return @[];
}

@end

रिश्ते वे वस्तुएँ हैं जो प्रतिक्रिया में नेस्टेड वस्तुओं का प्रतिनिधित्व करती हैं:

RelationshipObject.h

@interface RelationshipObject : NSObject

@property (nonatomic,copy) NSString* source;
@property (nonatomic,copy) NSString* destination;
@property (nonatomic) Class mappingClass;

+(RelationshipObject*)relationshipWithKey:(NSString*)key andMappingClass:(Class)mappingClass;
+(RelationshipObject*)relationshipWithSource:(NSString*)source destination:(NSString*)destination andMappingClass:(Class)mappingClass;

@end

RelationshipObject.m

@implementation RelationshipObject

+(RelationshipObject*)relationshipWithKey:(NSString*)key andMappingClass:(Class)mappingClass {
    RelationshipObject* object = [[RelationshipObject alloc] init];
    object.source = key;
    object.destination = key;
    object.mappingClass = mappingClass;
    return object;
}

+(RelationshipObject*)relationshipWithSource:(NSString*)source destination:(NSString*)destination andMappingClass:(Class)mappingClass {
    RelationshipObject* object = [[RelationshipObject alloc] init];
    object.source = source;
    object.destination = destination;
    object.mappingClass = mappingClass;
    return object;
}

@end

फिर मैं इस तरह RestKit के लिए मानचित्रण स्थापित कर रहा हूँ:

ObjectMappingInitializer.h

@interface ObjectMappingInitializer : NSObject

+(void)initializeRKObjectManagerMapping:(RKObjectManager*)objectManager;

@end

ObjectMappingInitializer.m

@interface ObjectMappingInitializer (Private)

+ (NSArray*)mappableClasses;

@end

@implementation ObjectMappingInitializer

+(void)initializeRKObjectManagerMapping:(RKObjectManager*)objectManager {

    NSMutableDictionary *mappingObjects = [NSMutableDictionary dictionary];

    // Creating mappings for classes
    for (Class mappableClass in [self mappableClasses]) {
        RKObjectMapping *newMapping = [RKObjectMapping mappingForClass:mappableClass];
        [newMapping addAttributeMappingsFromArray:[mappableClass fieldsArrayForMapping]];
        [newMapping addAttributeMappingsFromDictionary:[mappableClass fieldsDictionaryForMapping]];
        [mappingObjects setObject:newMapping forKey:[mappableClass description]];
    }

    // Creating relations for mappings
    for (Class mappableClass in [self mappableClasses]) {
        RKObjectMapping *mapping = [mappingObjects objectForKey:[mappableClass description]];
        for (RelationshipObject *relation in [mappableClass relationships]) {
            [mapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:relation.source toKeyPath:relation.destination withMapping:[mappingObjects objectForKey:[relation.mappingClass description]]]];
        }
    }

    // Creating response descriptors with mappings
    for (Class mappableClass in [self mappableClasses]) {
        for (NSString* pathPattern in [mappableClass pathPatterns]) {
            if ([mappableClass keyPathes]) {
                for (NSString* keyPath in [mappableClass keyPathes]) {
                    [objectManager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:[mappingObjects objectForKey:[mappableClass description]] method:RKRequestMethodAny pathPattern:pathPattern keyPath:keyPath statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]];
                }
            } else {
                [objectManager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:[mappingObjects objectForKey:[mappableClass description]] method:RKRequestMethodAny pathPattern:pathPattern keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]];
            }
        }
    }

    // Error Mapping
    RKObjectMapping *errorMapping = [RKObjectMapping mappingForClass:[Error class]];
    [errorMapping addAttributeMappingsFromArray:[Error fieldsArrayForMapping]];
    for (NSString *pathPattern in Error.pathPatterns) {
        [[RKObjectManager sharedManager] addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:errorMapping method:RKRequestMethodAny pathPattern:pathPattern keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassClientError)]];
    }
}

@end

@implementation ObjectMappingInitializer (Private)

+ (NSArray*)mappableClasses {
    return @[
        [FruiosPaginationResults class],
        [FruioItem class],
        [Pagination class],
        [ContactInfo class],
        [Credentials class],
        [User class]
    ];
}

@end

MappableEntry कार्यान्वयन के कुछ उदाहरण:

User.h

@interface User : MappableEntity

@property (nonatomic) long userId;
@property (nonatomic, copy) NSString *username;
@property (nonatomic, copy) NSString *email;
@property (nonatomic, copy) NSString *password;
@property (nonatomic, copy) NSString *token;

- (instancetype)initWithUsername:(NSString*)username email:(NSString*)email password:(NSString*)password;

- (NSDictionary*)registrationData;

@end

User.m

@implementation User

- (instancetype)initWithUsername:(NSString*)username email:(NSString*)email password:(NSString*)password {
    if (self = [super init]) {
        self.username = username;
        self.email = email;
        self.password = password;
    }
    return self;
}

- (NSDictionary*)registrationData {
    return @{
        @"username": self.username,
        @"email": self.email,
        @"password": self.password
    };
}

+ (NSArray*)pathPatterns {
    return @[
        [NSString stringWithFormat:@"/api/%@/users/register", APIVersionString],
        [NSString stringWithFormat:@"/api/%@/users/login", APIVersionString]
    ];
}

+ (NSArray*)fieldsArrayForMapping {
    return @[ @"username", @"email", @"password", @"token" ];
}

+ (NSDictionary*)fieldsDictionaryForMapping {
    return @{ @"id": @"userId" };
}

@end

अब अनुरोध रैपिंग के बारे में:

मेरे पास सभी एपीयरेंस वर्गों में लाइन की लंबाई कम करने के लिए ब्लॉक की परिभाषा के साथ हेडर फाइल है:

APICallbacks.h

typedef void(^SuccessCallback)();
typedef void(^SuccessCallbackWithObjects)(NSArray *objects);
typedef void(^ErrorCallback)(NSError *error);
typedef void(^ProgressBlock)(float progress);

और मेरे APIRequest वर्ग का उदाहरण जो मैं उपयोग कर रहा हूं:

LoginAPI.h

@interface LoginAPI : NSObject

- (void)loginWithCredentials:(Credentials*)credentials onSuccess:(SuccessCallbackWithObjects)onSuccess onError:(ErrorCallback)onError;

@end

LoginAPI.m

@implementation LoginAPI

- (void)loginWithCredentials:(Credentials*)credentials onSuccess:(SuccessCallbackWithObjects)onSuccess onError:(ErrorCallback)onError {
    [[RKObjectManager sharedManager] postObject:nil path:[NSString stringWithFormat:@"/api/%@/users/login", APIVersionString] parameters:[credentials credentialsData] success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
        onSuccess(mappingResult.array);
    } failure:^(RKObjectRequestOperation *operation, NSError *error) {
        onError(error);
    }];
}

@end

और आप सभी को कोड में करने की जरूरत है, बस एपीआई ऑब्जेक्ट को इनिशियलाइज़ करें और जब भी ज़रूरत हो इसे कॉल करें:

SomeViewController.m

@implementation SomeViewController {
    LoginAPI *_loginAPI;
    // ...
}

- (void)viewDidLoad {
    [super viewDidLoad];

    _loginAPI = [[LoginAPI alloc] init];
    // ...
}

// ...

- (IBAction)signIn:(id)sender {
    [_loginAPI loginWithCredentials:_credentials onSuccess:^(NSArray *objects) {
        // Success Block
    } onError:^(NSError *error) {
        // Error Block
    }];
}

// ...

@end

मेरा कोड सही नहीं है, लेकिन एक बार सेट करना और विभिन्न परियोजनाओं के लिए उपयोग करना आसान है। अगर यह किसी के लिए दिलचस्प है, तो mb मैं कुछ समय व्यतीत कर सकता हूं और इसके लिए GitHub और CocoaPods पर कहीं एक सार्वभौमिक समाधान बना सकता हूं।


7

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

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

एक अन्य परियोजना में एक स्थानीय सुरक्षित डेटाबेस को लागू करना शामिल है जो नेटवर्क उपलब्ध होने पर पृष्ठभूमि में कंपनी सिस्टम के साथ सिंक्रनाइज़ होता है। मैंने एक बैकग्राउंड सिंक्रोनाइज़र बनाया जो रेस्टकिट का इस्तेमाल करता था क्योंकि ऐसा लगता था कि मुझे हर चीज़ की ज़रूरत थी। लेकिन मुझे idiosyncratic JSON से निपटने के लिए RestKit के लिए इतना कस्टम कोड लिखना पड़ा कि मैं अपने JSON को कोरडाटा ट्रांसफॉर्मेशन लिखकर यह सब जल्दी कर सकता था। हालांकि, ग्राहक इस ऐप को घर में लाना चाहते थे और मुझे लगा कि RestKit उन फ्रेमवर्क के समान होगा जो वे अन्य प्लेटफार्मों पर उपयोग करते थे। मैं यह देखने के लिए इंतजार कर रहा हूं कि क्या यह एक अच्छा निर्णय था।

फिर, मेरे लिए मुद्दा जरूरत पर ध्यान देना है और वास्तुकला को निर्धारित करना है। मैं तीसरे पक्ष के पैकेजों का उपयोग करने से बचने के लिए नरक की तरह प्रयास करता हूं क्योंकि वे लागतें लाते हैं जो केवल ऐप के थोड़ी देर के लिए क्षेत्र में रहने के बाद दिखाई देती हैं। मैं वर्ग पदानुक्रम बनाने से बचने की कोशिश करता हूं क्योंकि वे शायद ही कभी भुगतान करते हैं। अगर मैं एक पैकेज को अपनाने के बजाय उचित अवधि में कुछ लिख सकता हूं जो पूरी तरह से फिट नहीं है, तो मैं इसे करता हूं। मेरा कोड डिबगिंग और उचित टिप्पणी के लिए अच्छी तरह से संरचित है, लेकिन तीसरे पक्ष के पैकेज शायद ही कभी होते हैं। उस के साथ, मैं वायुसेना नेटवर्किंग भी उपेक्षा और अच्छी तरह से संरचित, अच्छी तरह से टिप्पणी की है, और बनाए रखने के लिए उपयोगी पाते हैं और मैं इसे एक बहुत का उपयोग करें! RestKit में बहुत सारे सामान्य मामले शामिल हैं, लेकिन मुझे लगता है कि जब मैं इसका इस्तेमाल करता हूं तो मैं एक लड़ाई में हूं, और अधिकांश डेटा स्रोत जिनका मैं सामना करता हूं, वे quirks और मुद्दों से भरे हुए हैं, जिन्हें कस्टम कोड के साथ सबसे अच्छी तरह से हैंडल किया जाता है। अपने पिछले कुछ एप्स में मैं सिर्फ JSON कन्वर्टर्स में निर्मित का उपयोग करता हूं और कुछ उपयोगिता विधियां लिखता हूं।

एक पैटर्न जो मैं हमेशा उपयोग करता हूं, वह है मुख्य थ्रेड से नेटवर्क कॉल प्राप्त करना। पिछले 4-5 ऐप मैंने डिस्पैच_स्रोत_क्रिएट का उपयोग करके एक बैकग्राउंड टाइमर टास्क सेट किया है जो हर बार उठता है और आवश्यकतानुसार नेटवर्क टास्क करता है। आपको कुछ थ्रेड सुरक्षा कार्य करने की आवश्यकता है और सुनिश्चित करें कि UI संशोधित कोड मुख्य थ्रेड को भेजा जाता है। यह आपके ऑनबोर्डिंग / इनिशियलाइज़ेशन को इस तरह से करने में मदद करता है कि उपयोगकर्ता बोझ या देरी महसूस नहीं करता है। अभी तक यह अच्छी तरह से काम कर रहा है। मैं इन बातों पर गौर करने का सुझाव देता हूं।

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


4

मैं उस दृष्टिकोण का उपयोग करता हूं जो मैंने यहां से प्राप्त किया है: https://github.com/Constantine-Fry/Foursquare-API-v2 । मैंने स्विफ्ट में उस लाइब्रेरी को फिर से लिखा है और आप कोड के इन हिस्सों से वास्तुशिल्प दृष्टिकोण देख सकते हैं:

typealias OpertaionCallback = (success: Bool, result: AnyObject?) -> ()

class Foursquare{
    var authorizationCallback: OperationCallback?
    var operationQueue: NSOperationQueue
    var callbackQueue: dispatch_queue_t?

    init(){
        operationQueue = NSOperationQueue()
        operationQueue.maxConcurrentOperationCount = 7;
        callbackQueue = dispatch_get_main_queue();
    }

    func checkIn(venueID: String, shout: String, callback: OperationCallback) -> NSOperation {
        let parameters: Dictionary <String, String> = [
            "venueId":venueID,
            "shout":shout,
            "broadcast":"public"]
        return self.sendRequest("checkins/add", parameters: parameters, httpMethod: "POST", callback: callback)
    }

    func sendRequest(path: String, parameters: Dictionary <String, String>, httpMethod: String, callback:OperationCallback) -> NSOperation{
        let url = self.constructURL(path, parameters: parameters)
        var request = NSMutableURLRequest(URL: url)
        request.HTTPMethod = httpMethod
        let operation = Operation(request: request, callbackBlock: callback, callbackQueue: self.callbackQueue!)
        self.operationQueue.addOperation(operation)
        return operation
    }

    func constructURL(path: String, parameters: Dictionary <String, String>) -> NSURL {
        var parametersString = kFSBaseURL+path
        var firstItem = true
        for key in parameters.keys {
            let string = parameters[key]
            let mark = (firstItem ? "?" : "&")
            parametersString += "\(mark)\(key)=\(string)"
            firstItem = false
        }
    return NSURL(string: parametersString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding))
    }
}

class Operation: NSOperation {
    var callbackBlock: OpertaionCallback
    var request: NSURLRequest
    var callbackQueue: dispatch_queue_t

    init(request: NSURLRequest, callbackBlock: OpertaionCallback, callbackQueue: dispatch_queue_t) {
        self.request = request
        self.callbackBlock = callbackBlock
        self.callbackQueue = callbackQueue
    }

    override func main() {
        var error: NSError?
        var result: AnyObject?
        var response: NSURLResponse?

        var recievedData: NSData? = NSURLConnection.sendSynchronousRequest(self.request, returningResponse: &response, error: &error)

        if self.cancelled {return}

        if recievedData{
            result = NSJSONSerialization.JSONObjectWithData(recievedData, options: nil, error: &error)
            if result != nil {
                if result!.isKindOfClass(NSClassFromString("NSError")){
                    error = result as? NSError
            }
        }

        if self.cancelled {return}

        dispatch_async(self.callbackQueue, {
            if (error) {
                self.callbackBlock(success: false, result: error!);
            } else {
                self.callbackBlock(success: true, result: result!);
            }
            })
    }

    override var concurrent:Bool {get {return true}}
}

असल में, वहाँ NSOperation उपवर्ग है जो NSURLRequest बनाता है, JSON प्रतिक्रिया देता है और कतार में परिणाम के साथ कॉलबैक ब्लॉक जोड़ता है। मुख्य एपीआई वर्ग NSURLRequest का निर्माण करता है, जो NSOperation उपवर्ग को आरंभ करता है और इसे कतार में जोड़ता है।


3

हम स्थिति के आधार पर कुछ दृष्टिकोणों का उपयोग करते हैं। अधिकांश चीजों के लिए AFNetworking सबसे सरल और सबसे मजबूत दृष्टिकोण है जिसमें आप हेडर सेट कर सकते हैं, मल्टीपार्ट डेटा अपलोड कर सकते हैं, GET, POST, PUT & DELETE का उपयोग कर सकते हैं और UIKit के लिए अतिरिक्त श्रेणियों का एक समूह है जो आपको उदाहरण के लिए एक छवि सेट करने की अनुमति देते हैं a url। बहुत सारे कॉल के साथ एक जटिल ऐप में हम कभी-कभी इसे अपनी खुद की एक सुविधा पद्धति के लिए अमूर्त कर देते हैं जो कुछ इस तरह होगा:

-(void)makeRequestToUrl:(NSURL *)url withParameters:(NSDictionary *)parameters success:(void (^)(id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;

ऐसी कुछ स्थितियाँ हैं जहाँ AFNetworking उपयुक्त नहीं है, हालाँकि जहाँ आप एक फ्रेमवर्क या अन्य पुस्तकालय घटक बना रहे हैं, क्योंकि AFNetworking पहले से ही किसी अन्य कोड बेस में हो सकता है। इस स्थिति में आप एक NSMutableURLRequest इनलाइन का उपयोग करेंगे यदि आप एक एकल कॉल कर रहे हैं या एक अनुरोध / प्रतिक्रिया वर्ग में सार कर रहे हैं।


मेरे लिए यह सबसे अच्छा और स्पष्ट उत्तर है, चीयर्स। "यह इत्ना आसान है"। @ स्मार्टिन, व्यक्तिगत रूप से हम बस हर समय NSMutableURLRequest का उपयोग करते हैं; क्या AFNetworking का उपयोग करने का कोई वास्तविक कारण है?
फटी

AFNetworking वास्तव में सुविधाजनक है। मेरे लिए सफलता और असफल ब्लॉक अगर लायक है, जबकि यह कोड को प्रबंधित करना आसान बनाता है। मैं मानता हूं कि यह कभी-कभी कुल ओवरकिल है।
मार्टिन

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

2

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

User* newUser = [User createInManagedObjectContext:managedObjectContext];
[newUser postOnSuccess:^(...) { ... } onFailure:^(...) { ... }];

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

NSManagedObject + एक्सटेंशन में:

+ (instancetype)createInContext:(NSManagedObjectContext*)context
{
    NSAssert(context.persistentStoreCoordinator.managedObjectModel.entitiesByName[[self entityName]] != nil, @"Entity with name %@ not found in model. Is your class name the same as your entity name?", [self entityName]);
    return [NSEntityDescription insertNewObjectForEntityForName:[self entityName] inManagedObjectContext:context];
}

NSManagedObject + Networking.m में:

- (void)getOnSuccess:(RESTSuccess)onSuccess onFailure:(RESTFailure)onFailure blockInput:(BOOL)blockInput
{
    [[RKObjectManager sharedManager] getObject:self path:nil parameters:nil success:onSuccess failure:onFailure];
    [self handleInputBlocking:blockInput];
}

जब आप श्रेणियों के माध्यम से एक सामान्य आधार वर्ग की कार्यक्षमता का विस्तार कर सकते हैं तो अतिरिक्त सहायक कक्षाएं क्यों जोड़ें?

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


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

0

प्रयत्न Https://github.com/kevin0571/STNetTaskQueue

अलग कक्षाओं में एपीआई अनुरोध बनाएँ।

STNetTaskQueue थ्रेडिंग और डेलीगेट / कॉलबैक से निपटेगा।

विभिन्न प्रोटोकॉल के लिए विस्तार योग्य।


0

विशुद्ध रूप से वर्ग के डिजाइन के नजरिए से, आपके पास आमतौर पर ऐसा कुछ होगा:

  • आपका दृश्य नियंत्रक एक या अधिक दृश्य नियंत्रित करता है
  • डेटा मॉडल वर्ग - यह वास्तव में इस बात पर निर्भर करता है कि आप कितनी वास्तविक विशिष्ट संस्थाओं के साथ काम कर रहे हैं, और वे कैसे संबंधित हैं।

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

    डेटा मॉडल कक्षाएं न केवल डेटा प्रदर्शित करने में काम आएंगी, बल्कि उन्हें क्रमबद्ध भी करेंगी, जिसमें उनमें से प्रत्येक JSON / XML / CSV (या कुछ और) निर्यात विधियों के माध्यम से अपने स्वयं के क्रमांकन प्रारूप को उजागर कर सकता है।

  • यह समझना महत्वपूर्ण है कि आपको एपीआई अनुरोध बिल्डर वर्गों की आवश्यकता है जो सीधे आपके REST API समापन बिंदु के साथ मैप करते हैं। मान लें कि आपके पास एक API है जो उपयोगकर्ता को लॉग इन करता है - इसलिए आपका लॉगिन API बिल्डर वर्ग लॉगिन एपीआई के लिए POST JSON पेलोड बनाएगा। एक अन्य उदाहरण में, कैटलॉग आइटम की सूची के लिए एक एपीआई अनुरोध बिल्डर वर्ग एपीआई संबंधित जीआईटी के लिए जीईटी क्वेरी स्ट्रिंग बनाएगा और रीस्ट जीईटी क्वेरी को आग देगा।

    ये एपीआई अनुरोध बिल्डर कक्षाएं आमतौर पर दृश्य नियंत्रकों से डेटा प्राप्त करेंगी और यूआई अपडेट / अन्य संचालन के लिए नियंत्रकों को देखने के लिए वापस उसी डेटा को भी पास करेंगी। फिर कंट्रोलर तय करेंगे कि डेटा मॉडल ऑब्जेक्ट को उस डेटा के साथ कैसे अपडेट किया जाए।

  • अंत में, REST क्लाइंट का दिल - एपीआई डेटा भ्रूण वर्ग जो आपके एप्लिकेशन को सभी प्रकार के एपीआई अनुरोधों से अनजान है। यह वर्ग अधिक संभावना एक सिंगलटन होगा, लेकिन जैसा कि अन्य ने बताया है, यह एक सिंगलटन होना नहीं है।

    ध्यान दें कि लिंक केवल एक विशिष्ट कार्यान्वयन है और सत्र, कुकीज़ आदि जैसे परिदृश्य पर ध्यान नहीं देता है, लेकिन यह आपको किसी भी 3 पार्टी फ्रेमवर्क का उपयोग किए बिना प्राप्त करने के लिए पर्याप्त है।


0

इस सवाल के बहुत सारे उत्कृष्ट और व्यापक उत्तर पहले से ही हैं, लेकिन मुझे लगता है कि मुझे इसका उल्लेख करना होगा क्योंकि किसी और के पास नहीं है।

स्विफ्ट के लिए अलमोफायर। https://github.com/Alamofire/Alamofire

यह AFNetworking के रूप में एक ही लोगों द्वारा बनाया गया है, लेकिन अधिक सीधे स्विफ्ट को ध्यान में रखकर बनाया गया है।


0

मुझे लगता है कि अब के लिए मध्यम परियोजना एमवीवीएम वास्तुकला और बिग परियोजना वीआईपी आर्किटेक्चर का उपयोग करती है और हासिल करने की कोशिश करती है

  • प्रोटोकॉल उन्मुख प्रोग्रामिंग
  • सॉफ्टवेयर डिजाइन पैटर्न
  • सिद्ध सिद्धांत
  • सामान्य प्रोग्रामिंग
  • अपने आप को मत दोहराओ (DRY)

और iOS नेटवर्किंग एप्लिकेशन (REST क्लाइंट) के निर्माण के लिए आर्किटेक्चरल दृष्टिकोण

साफ और पठनीय कोड के लिए पृथक्करण चिंता दोहराव से बचें:

import Foundation
enum DataResponseError: Error {
    case network
    case decoding

    var reason: String {
        switch self {
        case .network:
            return "An error occurred while fetching data"
        case .decoding:
            return "An error occurred while decoding data"
        }
    }
}

extension HTTPURLResponse {
    var hasSuccessStatusCode: Bool {
        return 200...299 ~= statusCode
    }
}

enum Result<T, U: Error> {
    case success(T)
    case failure(U)
}

निर्भरता उलटा

 protocol NHDataProvider {
        func fetchRemote<Model: Codable>(_ val: Model.Type, url: URL, completion: @escaping (Result<Codable, DataResponseError>) -> Void)
    }

मुख्य जिम्मेदार:

  final class NHClientHTTPNetworking : NHDataProvider {

        let session: URLSession

        init(session: URLSession = URLSession.shared) {
            self.session = session
        }

        func fetchRemote<Model: Codable>(_ val: Model.Type, url: URL,
                             completion: @escaping (Result<Codable, DataResponseError>) -> Void) {
            let urlRequest = URLRequest(url: url)
            session.dataTask(with: urlRequest, completionHandler: { data, response, error in
                guard
                    let httpResponse = response as? HTTPURLResponse,
                    httpResponse.hasSuccessStatusCode,
                    let data = data
                    else {
                        completion(Result.failure(DataResponseError.network))
                        return
                }
                guard let decodedResponse = try? JSONDecoder().decode(Model.self, from: data) else {
                    completion(Result.failure(DataResponseError.decoding))
                    return
                }
                completion(Result.success(decodedResponse))
            }).resume()
        }
    }

आप यहां पाएंगे कि बाकी एपीआई स्विफ्ट प्रोजेक्ट के साथ GitHub MVVM आर्किटेक्चर है


0

मोबाइल सॉफ्टवेयर इंजीनियरिंग में, सबसे व्यापक रूप से उपयोग किया जाने वाला क्लीन आर्किटेक्चर + MVVM और Redux पैटर्न हैं।

स्वच्छ वास्तुकला + MVVM 3 परतों से मिलकर बनता है: डोमेन, प्रस्तुति, डेटा परतें। जहां प्रेजेंटेशन लेयर और डेटा रिपॉजिटरी लेयर डोमेन लेयर पर निर्भर करते हैं:

Presentation Layer -> Domain Layer <- Data Repositories Layer

और प्रस्तुति परत में ViewModels और दृश्य (MVVM) शामिल हैं:

Presentation Layer (MVVM) = ViewModels + Views
Domain Layer = Entities + Use Cases + Repositories Interfaces
Data Repositories Layer = Repositories Implementations + API (Network) + Persistence DB

इस लेख में स्वच्छ वास्तुकला + MVVM https://tech.olx.com/clean-Healthecture-and-mvvm-on-ios-c9d167d9f5b3 पर अधिक विस्तृत वर्णन है

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.