Հաջորդը, ես առաջացել եմ JSON decoding- ի հետ, ինչպիսիք են Swift- ում: Որպես շատ մշակողներ, երբ առաջանում են մեծ, պլաստիկ JSON- ի պատասխանը, իմ առաջին հզորությունը էր, որ հասկանում եմ «լուսանկարներ» գործիքները: Ես ուզում եմ տեսնել, թե ինչպես են մեր հայտնի առցանց ռեսուրսները, ինչպիսիք են: , տարբեր JSON-to-Swift փոխանակիչներ, եւ նույնիսկ आधुनिक AI մոդելներ - կարող են վերահսկել սխալ, repetitive տվյալների կառուցվածքը. Quicktype Իհարկե, որ ճշգրիտ եմ, ես ամբողջականապես կախված եմ. The Problem: The “Flat” JSON Սխալը Այս խնդիրը կատարվում է այն ժամանակ, երբ դուք հանդիպում եք հագուստ API-ի կամ սխալ ձեւավորվող պատասխանի հետ, որը օգտագործում է «պլետիկ» թվարկված բաղադրիչները, այլեւ չժանգոտվող ալիքները: Տեսեք այս 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 } ] } Ինչու է Online Converter- ը սխալում Երբ ես սեղմել եմ այս ստանդարտ փոխանակման գործիքները, արդյունքը էր սպասարկման սխալը: Նրանք ստեղծել են «մարթ բաղադրիչների», որը տեսնում է ինչ-որ բան այսպես: 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+ ընտրական բաղադրիչները: Դա երազանք է սպասարկման եւ կախված է ձեր մասնագիտական ռեժիմը: Ես որոշել եմ վերահսկել decoding գործընթացը, որպեսզի այն clean, Swifty, եւ - առավել կարեւոր - Այսպիսին է, թե ինչպես ես կառուցել լուծումը եւ թե ինչու դա աշխատում է: production-ready The Secret Weapon: Ինչու մենք օգտագործում ենք Struct համար CodingKeys 99 %-ի Swift- ի դասընթացները, դուք տեսնում եք Հիմնականը defined as an Enums մեծ է, երբ դուք գիտեք, յուրաքանչյուր միավոր սեղմիչը compile ժամանակ. Բայց մեր դեպքում, մենք ունենք «Flat» JSON հետ սեղմիչներ, ինչպիսիք են Հիմա ... մինչեւ «Ինչու՞ն է, որ 40 սխալով գրել է սխալը, ոչ միայն սխալ է, այլեւ սխալ է սխալը»: Արդյոք՝ CodingKeys enum strIngredient1 strIngredient2 20 struct 1. Պտուտակային պահանջների բացառումը Համատեղելի է Մարդը պետք է աշխատել երկու եւ Արդյոք, օգտագործելով մի struct, մենք կարող ենք անցնել Սեղմեք Initializer- ը Runtime- ում 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 } «Այսը» անունները «Այսը» անունները Դուք չեք պետք է վերցնել API- ի անունագրման պայմանները ձեր app- ում: Նշեք, թե ինչպես ես օգտագործել եմ Այսպիսով, վերադառնալը decoding logic կարող է կարդալ, իսկ պահել է «հավատ» API սեղմիչներ ներսում այս struct. static var static var name = CodingKeys(rawValue: "strMeal") static var thumb = CodingKeys(rawValue: "strMealThumb") static var instructions = CodingKeys(rawValue: "strInstructions") 3. Dynamic Key Generation-ի հզորությունը Դա այն մասը է, որը կատարում է այս մեթոդը ավելի բարձր է, քան որեւէ AI- ի արտադրված կոդը: Մենք ստեղծել ենք ստատիկ գործառույթներ, որոնք օգտագործում են Տեղադրելու համար կոճակը կոճի վրա. string interpolation static func strIngredient(_ index: Int) -> Self { CodingKeys(rawValue: "strIngredient\(index)") } static func strMeasure(_ index: Int) -> Self { CodingKeys(rawValue: "strMeasure\(index)") } Մինչեւ hardcoding Հիմա , եւ այլն, մենք այժմ ունենք «կիալ գործարանը». Մեր Initializer- ում, մենք պարզապես կոչում ենք այդ գործառույթները: Դա clean է, այն վերլուծելի է, եւ այն շատ դժվար է տպագրել, քան գրել 40 մասնավոր գործառույթներ: strIngredient1 strIngredient2 1...20 4. Բարձրացնել մոդել, որը իրականում նշանակում է Հիմնական JSON- ը մի բաղադրիչի եւ նրա չափման համար բաղադրում է, ինչպիսիք են երկու անհատներ, ովքեր ապրում են տարբեր տաններում: Մեր app- ում, այն մի քանի է: Հիմնական struct- ի բաղադրիչով, մենք վերահսկում ենք տվյալների բաղադրիչը աղբյուրում: struct Ingredient: Decodable, Hashable { let id: Int let name: String let measure: String } Ինչու եւ The ? Հիմնական ID Հիմնական ID Ես ավելացել եմ Ինչպե՞ս կարող եք վերցնել SwiftUI- ը, ինչպիսիք են եւ Հիմնական տեղեկատվություն: Հիմնական տեղեկատվություն Մենք ապահովում ենք: id List ForEach Hashable Ոչ UI Glitches: SwiftUI- ը չի կախվածվի, եթե երկու տարբեր բաղադրիչները ունեն նույն անունը (կամ երկու տարբեր տեսակի "Salt"). Ապրանքանիշներ: Diffable data sources love hashable objects. «Այսը» «Այսը» «Այսը» «Այսը» Երբ մենք ստանում ենք initializer, տեսնել, թե ինչպես մենք սահմանում ենք մեր հիմնական բաղադրիչները. Մենք ոչ միայն վերլուծում ենք, թե ինչ է API-ը տալիս մեզ, մենք փոխել է այն . Clean Swift let name: String let thumb: URL? let instructions: String let ingredients: [Ingredient] Ավելի լավ է, քան strMeal.We dropped the Hungarian notation. name is better than strMeal. Արդյոք, եթե API-ը ուղեցնում է սեղմված կապը կամ սեղմված գիծը, մեր decoder- ը սեղմում է այն գեղեցիկորեն փոստի ֆիզայնի ժամանակ, ոչ հետո View- ում: The Smart Initializer: Մեր «Data Bouncer» Այսպիսով, այն ամենը, ինչ 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 } The Final Result: Clean, Swifty եւ UI-ready Մինչեւ ամեն ինչ աշխատում է պոռնկոցներում, տեսնեք, թե ինչ ենք կատարում: Մենք փոխել ենք «լայն» JSON սխալը մոդելով, որը երջանիկ է օգտագործել: Սա այն է, ինչ ձեր app- ի մյուսը տեսնում է հիմա: struct MealDetail { let name: String let instructions: String let thumb: URL? let ingredients: [Ingredient] } Պարզություն է UI- ում Քանի որ մենք անում ենք խոշոր վերահսկողությունը decoding ֆիզայնի ընթացքում - փաթեթավորելով խոշոր արժեքները եւ խոշորում բաղադրիչները - մեր SwiftUI կոդը ստանում է հեշտությամբ: Մենք չենք պետք որեւէ բաղադրիչ բաղադրիչը View- ում: Մենք պարզապես մուտքագրում ենք տվյալները ուղղակիորեն բաղադրիչներին: The Cherry on Top: Կարդացուցել հեշտությամբ Դուք կարող եք տեսնել մի փոքր բջջային ազդեցություն: երբ մենք որոշում ենք բջջային բջջային , Swift- ը կանգնում է ստեղծել ստանդարտ memberwise initializer- ը: Այսպիսով կարող է գրել միավոր փորձեր կամ SwiftUI Previews- ը մի քիչ վախենում: init(from: Decoder) Ինչպե՞ս վերահսկել այս խնդիրը եւ վերահսկել մեր կոդը բազանը «պատեգորիաների համար», մենք կարող ենք ավելացնել այս պարզ լրացուցիչը: Այսպիսով մենք կարող ենք ստեղծել «Mock» տվյալները մեր UI-ի համար, առանց պետք է 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 } } Այժմ, ստեղծել preview այնքան հեշտ է, քանի որ: let mock = MealDetail(name: "Pasta", thumb: nil, instructions: "Cook it.", ingredients: []) Հյուրատետր Հաջորդ անգամ, երբ դուք հանդիպում եք սխալ API-ի հետ, հիշեք: Online գործիքներ եւ AI- ը կարող է տալիս Ձեզ արագ "copy-paste" լուծում, բայց նրանք հաճախ առաջանում են տեխնիկական ծախսերը: Արդյոք, դուք կարող եք ստեղծել կոդը, որը: don’t let the backend dictate your frontend architecture. Decodable Գիտելի: Հասկած, նպատակային հիմնված բիզնեսի անուններ. Տեղադրում է սեղմված կամ սեղմված տվյալները. Ավելի հեշտ է փորձել եւ հեշտ է տեսնել UI- ում: Happy coding, եւ keep your models clean! Full Code- ը այստեղ է: https://github.com/PavelAndreev13/NetworkLayer