मैंने हाल ही में Swift में JSON डिकोडिंग के साथ एक दिलचस्प चुनौती का सामना किया. कई डेवलपर्स की तरह, जब एक बड़ी, जटिल JSON प्रतिक्रिया का सामना करना पड़ता है, तो मेरा पहला प्रवृत्ति "खेल फिक्स" उपकरणों के लिए पहुंचना था. मैं देखना चाहता था कि हमारे लोकप्रिय ऑनलाइन संसाधन - जैसे विभिन्न JSON-to-Swift कनवर्टर, और यहां तक कि आधुनिक एआई मॉडल भी - एक गड़बड़ी, दोहराने वाली डेटा संरचना का संचालन करेंगे। Quicktype सच कहूं तो मैं पूरी तरह से निराश हो गया था। The Problem: The “Flat” JSON का बुरा सपना समस्या तब उत्पन्न होती है जब आप एक प्राचीन एपीआई या एक बुरी तरह संरचित प्रतिक्रिया का सामना करते हैं जो साफ रेंज के बजाय "फ्लैट" नंबरित गुणों का उपयोग करता है। { "meals": [ { "idMeal": "52771", "strMeal": "Spicy Arrabiata Penne", "strInstructions": "Bring a large pot of water to a boil...", "strMealThumb": "https://www.themealdb.com/images/media/meals/ustsqw1468250014.jpg", "strIngredient1": "penne rigate", "strIngredient2": "olive oil", "strIngredient3": "garlic", "strIngredient4": "chopped tomatoes", "strIngredient5": "red chilli flakes", // ... this continues up to strIngredient20 "strMeasure1": "1 pound", "strMeasure2": "1/4 cup", "strMeasure3": "3 cloves", // ... this continues up to strMeasure20 } ] } क्यों ऑनलाइन कनवर्टर असफल जब मैंने इसे मानक रूपांतरण उपकरणों में कनेक्ट किया, तो परिणाम एक रखरखाव बुरा सपना था. उन्होंने एक "आधारित संपत्ति की दीवार" उत्पन्न की जो कुछ इस तरह दिखती थी: struct Meal: Codable { let idMeal: String let strMeal: String let strInstructions: String? let strMealThumb: String? // The repetitive property nightmare let strIngredient1: String? let strIngredient2: String? let strIngredient3: String? // ... let strIngredient20: String? let strMeasure1: String? let strMeasure2: String? let strMeasure3: String? // ... let strMeasure20: String? } चलो ईमानदार बनें, उन ऑनलाइन उपकरणों द्वारा उत्पन्न कोड किसी भी गंभीर परियोजना के लिए "रैशबन" में शामिल है. न केवल यह अविश्वसनीय है, बल्कि एक पीआर समीक्षा के दौरान अपने वरिष्ठ डेवलपर्स के चेहरे पर देखने की कल्पना करें जब वे 40+ वैकल्पिक संपत्ति देखते हैं. यह एक रखरखाव बुरा सपना है और आपकी पेशेवर प्रतिष्ठा को एक झटका है. मैं इसे साफ करने के लिए डिकोडिंग प्रक्रिया को नियंत्रित करने का फैसला किया, Swifty, और - सबसे महत्वपूर्ण बात - यहां यह है कि मैंने समाधान को कैसे संरचित किया और यह क्यों काम करता है। production-ready गुप्त हथियार: क्यों हम कोडिंगकेयर के लिए एक संरचना का उपयोग करते हैं 99% स्विफ्ट ट्यूटोरियल में, आप देखते हैं एक के रूप में परिभाषित Enums महान हैं जब आप संकलित समय पर प्रत्येक एकल कुंजी को जानते हैं. लेकिन हमारे मामले में, हमारे पास ऐसे कुंजी के साथ एक "फ्लैट" JSON है , ऊपर से 40 मामलों के साथ एक एनिम लिखना न केवल उबाऊ है - यह खराब इंजीनियरिंग है। इसके बजाय CodingKeys enum strIngredient1 strIngredient2 20 struct 1. प्रोटोकॉल की आवश्यकताओं का उल्लंघन इसके अनुरूप एक आदमी को दोनों को संभालना चाहिए और एक संरचना का उपयोग करके, हम पारित कर सकते हैं Runtime पर Initializer पर String करें। CodingKey String Int किसी struct CodingKeys: CodingKey { let stringValue: String var intValue: Int? init?(stringValue: String) { self.stringValue = stringValue } // This allows us to map any raw string from the JSON to our logic init(rawValue: String) { self.stringValue = rawValue } init?(intValue: Int) { return nil } // We don't need integer keys here } 2. साफ नामों के लिए "नकली" कुंजीों को मानचित्रित करना आपको अपने ऐप के अंदर एपीआई के नामकरण परंपराओं को पकड़ने की ज़रूरत नहीं है. ध्यान दें कि मैंने कैसे इस्तेमाल किया यह डिकोडिंग तर्क के बाकी हिस्सों को पढ़ने योग्य बनाए रखता है जबकि इस संरचना के अंदर "बुरे" एपीआई कुंजी को अलग रखता है। static var static var name = CodingKeys(rawValue: "strMeal") static var thumb = CodingKeys(rawValue: "strMealThumb") static var instructions = CodingKeys(rawValue: "strInstructions") 3. गतिशील कुंजी पीढ़ी की शक्ति यह वह हिस्सा है जो इस दृष्टिकोण को किसी भी एआई द्वारा उत्पन्न कोड से बेहतर बनाता है। उड़ान पर कुंजी बनाने के लिए। string interpolation static func strIngredient(_ index: Int) -> Self { CodingKeys(rawValue: "strIngredient\(index)") } static func strMeasure(_ index: Int) -> Self { CodingKeys(rawValue: "strMeasure\(index)") } हार्डकोडिंग के बजाय , , आदि, अब हमारे पास एक "कुंजी कारखाने" है। हमारे प्रारंभिकर में, हम बस इन कार्यों को बुलाते हैं. यह साफ है, यह पुनः उपयोग किया जा सकता है, और 40 व्यक्तिगत मामलों को लिखने की तुलना में एक टाइप करना काफी मुश्किल है। strIngredient1 strIngredient2 1...20 4. एक मॉडल का निर्माण जो वास्तव में समझ में आता है मूल JSON एक सामग्री और इसकी माप को अलग-अलग घरों में रहने वाले दो अजनबियों की तरह व्यवहार करता है. हमारे ऐप में, एक जोड़े हैं. एक समर्पित संरचना को निहित करके, हम स्रोत पर डेटा वास्तुकला को ठीक करते हैं: struct Ingredient: Decodable, Hashable { let id: Int let name: String let measure: String } क्यों और यह ? Hashable आईडी होशियार आईडी मैंने एक जोड़ा इस तरह के सिस्टम का उपयोग क्यों किया जाता है? क्योंकि आधुनिक SwiftUI दृष्टिकोण जैसे और डेटा को पहचानने की जरूरत है. हम सुनिश्चित करते हैं: id List ForEach Hashable कोई यूआई गड़बड़ी नहीं: SwiftUI भ्रमित नहीं होगा यदि दो अलग-अलग सामग्री के पास एक ही नाम है (जैसे दो अलग-अलग प्रकार के "साल")। प्रदर्शन: कमजोर डेटा स्रोत हैश योग्य वस्तुओं से प्यार करते हैं। 5. “एपीआई गंध” को साफ करना इससे पहले कि हम प्रारंभिकर तक पहुंच जाएं, देखें कि हम अपने मुख्य गुणों को कैसे परिभाषित करते हैं. हम न केवल एपीआई द्वारा हमें जो दिया गया है उसे कॉपी नहीं कर रहे हैं; हम इसे अनुवाद कर रहे हैं . Clean Swift let name: String let thumb: URL? let instructions: String let ingredients: [Ingredient] अलविदा str पूर्वावलोकन: हमने हंगरी का उल्लेख छोड़ दिया. नाम strMeal से बेहतर है। सही प्रकार: हम सीधे एक URL में छोटे चित्र को डिकोड करते हैं? यदि एपीआई एक टूटे हुए लिंक या एक खाली श्रृंखला भेजता है, तो हमारे डिकोडर इसे पेंसिंग चरण के दौरान आकर्षक रूप से संभालते हैं, बाद में दृश्य में नहीं। 6. स्मार्ट Initializer: हमारा “डेटा बोनसर” JSON की पेशकश की प्रत्येक कुंजी को अंधा रूप से स्वीकार करने के बजाय, हमारा कस्टम एक क्लब में एक बैनर की तरह कार्य करता है - केवल वैध डेटा प्रवेश करता है। init(from:) init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) // 1. Decode simple properties using our clean aliases self.name = try container.decode(String.self, forKey: .name) self.thumb = try? container.decode(URL.self, forKey: .thumb) self.instructions = try container.decode(String.self, forKey: .instructions) // 2. The Dynamic Decoding Loop var ingredients: [Ingredient] = [] for index in 1...20 { // We use 'try?' because some keys might be null or missing if let name = try? container.decode(String.self, forKey: .strIngredient(index)), let measure = try? container.decode(String.self, forKey: .strMeasure(index)), !name.isEmpty, !measure.isEmpty { // We only save it if the name AND measure are valid and non-empty ingredients.append(Ingredient(id: index, name: name, measure: measure)) } } self.ingredients = ingredients } अंतिम परिणाम: साफ, त्वरित, और यूआई-सक्षम आखिरकार, जो कुछ भी मंचों के पीछे काम करता है, उस पर एक नज़र डालें कि हमने क्या हासिल किया है. हमने एक "फ्लैट" JSON बुरे सपने को एक मॉडल में बदल दिया है जो उपयोग करने के लिए एक खुशी है. यह है कि आपकी ऐप के बाकी हिस्से अब क्या देखते हैं: struct MealDetail { let name: String let instructions: String let thumb: URL? let ingredients: [Ingredient] } UI में शुद्ध सरलता क्योंकि हमने डिकोडिंग चरण के दौरान भारी लिफ्ट किया - खाली मूल्यों को फ़िल्टर करना और सामग्री को समूह करना - हमारे SwiftUI कोड अविश्वसनीय रूप से साफ हो जाता है। शीर्ष पर चेरी: आसानी से मजाक बनाना आप एक छोटा सा सा साइड इफेक्ट देख सकते हैं: जब हम एक आदत को परिभाषित करते हैं , Swift डिफ़ॉल्ट सदस्य रूप से प्रारंभिक निर्माता उत्पन्न करना बंद कर देता है. यह लिखने इकाई परीक्षण या SwiftUI पूर्वावलोकन को थोड़ा परेशान कर सकता है. init(from: Decoder) इसे ठीक करने के लिए और हमारे कोडबेस को " परीक्षण अनुकूल" रखने के लिए, हम इस सरल एक्सटेंशन को जोड़ सकते हैं. यह हमें एक JSON फ़ाइल की आवश्यकता के बिना हमारे यूआई के लिए "मोक" डेटा बनाने की अनुमति देता है. extension MealDetail { // Restoring the ability to create manual instances for Mocks and Tests init(name: String, thumb: URL?, instructions: String, ingredients: [Ingredient]) { self.name = name self.thumb = thumb self.instructions = instructions self.ingredients = ingredients } } अब, एक पूर्वावलोकन बनाना उतना ही सरल है जैसे: let mock = MealDetail(name: "Pasta", thumb: nil, instructions: "Cook it.", ingredients: []) निष्कर्ष अगली बार जब आप एक गड़बड़ी API का सामना कर रहे हैं, याद रखें: ऑनलाइन उपकरण और एआई आपको एक त्वरित "कॉपी-पेस्ट" समाधान दे सकते हैं, लेकिन वे अक्सर तकनीकी ऋण का कारण बनते हैं। निष्पादित करने के लिए, आप कोड बनाते हैं जो: don’t let the backend dictate your frontend architecture. Decodable पढ़ने योग्य: स्पष्ट, इरादे पर आधारित संपत्ति के नाम। मजबूत: स्रोत पर खाली या क्षतिग्रस्त डेटा को फ़िल्टर करता है। बनाए रखने योग्य: परीक्षण करना आसान और यूआई में प्रदर्शित करना आसान। सफल कोडिंग, और अपने मॉडल साफ रखें! पूरा कोड यहाँ है: https://github.com/PavelAndreev13/NetworkLayer