Bekerja dengan objek JSON yang kompleks di Swift (Dapat dikodekan)

Saya diminta untuk menulis artikel ini oleh gangguan saraf yang hampir disebabkan oleh keinginan saya untuk belajar bagaimana berkomunikasi dengan API pihak ketiga, saya secara khusus tertarik pada proses decoding dokumen JSON! Untungnya, saya menghindari gangguan saraf, jadi sekarang saatnya berkontribusi ke komunitas dan mencoba menerbitkan artikel pertama saya di Habré.





Mengapa ada masalah dengan tugas yang begitu sederhana?





Untuk memahami dari mana masalah itu berasal, pertama-tama Anda perlu membicarakan tentang toolkit yang saya gunakan. Untuk memecahkan kode objek JSON, saya menggunakan protokol yang disintesis dari pustaka Foundation yang relatif baru - Modable .





Codable adalah protokol bawaan yang memungkinkan Anda melakukan encoding ke objek format teks dan mendekode dari format teks. Codable adalah jumlah dari dua protokol lainnya: Decodable dan Encodable.





Perlu membuat reservasi bahwa protokol dipanggil disintesis ketika beberapa metode dan propertinya memiliki implementasi default. Mengapa protokol seperti itu dibutuhkan? Agar lebih mudah bekerja dengan menandatanganinya, setidaknya dengan mengurangi jumlah kode boilerplate.





Protokol ini juga memungkinkan Anda bekerja dengan komposisi, bukan warisan!





Sekarang mari kita bicara tentang masalahnya:





  • -, , Apple. " "? JSON ; .





  • -, . , . .





  • -,





. : API Flickr, N- ( : ) .





: API, REST- API, , c GET-.





Flickr JSON :





{
   "photos":{
      "page":1,
      "pages":"11824",
      "perpage":2,
      "total":"23648",
      "photo":[
         {
            "id":"50972466107",
            "owner":"191126281@N@7" ,
            "secret":"Q6f861f8b0",
            "server":"65535",
            "farm":66,
            "title":"Prompt & Reliable Electrolux Oven Repairs in Gold Coast",
            "ispublic":1,
            "isfriend":0,
            "isfamily":0
         },
         {
            "id":"50970556873",
            "owner":"49965961@NG0",
            "secret":"21f7a6424b",
            "server":"65535",
            "farm" 66,
            "title":"IMG_20210222_145514",
            "ispublic":1,
            "isfriend":0,
            "isfamily":0
         }
      ]
   },
   "stat":"ok"
}

      
      



, . ? , . ? - , ( , ). - , , , ( ) () Flickr , .





? , GET- , ! : ---. . . JSON- , , . "photo": "id", "secret", "server"





, :





struct Photo {
    let id: String
    let secret: String
    let server: String
}

let results: [Photos] = // ...
      
      



"" . , best practices JSON- .





. , . , Codable. , .





{
   "id":"50972466107",
   "owner":"191126281@N07",
   "secret":"06f861f8b0"
}
      
      



. , JSON- ( "id", "secret", "server"); , (, ). Decodable, , ( , ). ? , " ". . ( , Data, decode(...) JSONDecoder Data).





:





  • , , API - jsonplaceholder.typicode.com, JSON-, GET-.





  • jsonformatter.curiousconcept.com . "" REST , Playground Xcode.





  • tool - app.quicktype.io - Swift JSON-.





. :





struct Photo: Decodable {
    let id: String
    let secret: String
    let server: String
}

let json = """
{
   "id":"50972466107",
   "owner":"191126281@N07",
   "secret":"06f861f8b0"
}
"""

let data = json.data(using: .utf8)
let results: Photo = try! JSONDecoder().decode(Photo.self, from: data)

      
      



, JSON-, "key" : "sometexthere" Decodable String, run-time. Decodable coerce- ( ).





struct Photo: Decodable {
    let id: Int
    let secret: String
    let server: Int
}

let json = """
{
   "id":"50972466107",
   "owner":"191126281@N07",
   "secret":"06f861f8b0"
}
"""

let data = json.data(using: .utf8)
let results: Photo = try! JSONDecoder().decode(Photo.self, from: data)

      
      



. ?





    {
       "id":"50972466107",
       "owner":"191126281@N07",
       "secret":"06f861f8b0",
       "server":"65535",
       "farm":66,
       "title":"Prompt & Reliable Electrolux Oven Repairs in Gold Coast",
       "ispublic":1,
       "isfriend":0,
       "isfamily":0
    }
      
      



, Decodable , , " " , . , API , " " , , , , . - "".





. JSON-:





[
    {
       "id":"50972466107",
       "owner":"191126281@N07",
       "secret":"06f861f8b0",
       "server":"65535",
       "farm":66,
       "title":"Prompt & Reliable Electrolux Oven Repairs in Gold Coast",
       "ispublic":1,
       "isfriend":0,
       "isfamily":0
    },
    {
       "id":"50970556873",
       "owner":"49965961@N00",
       "secret":"21f7a6524b",
       "server":"65535",
       "farm":66,
       "title":"IMG_20210222_145514",
       "ispublic":1,
       "isfriend":0,
       "isfamily":0
    }
]
      
      



( ) . , - !





struct Photo: Decodable {
    let id: String
    let secret: String
    let server: String
}

let json = """
[
    {
       "id":"50972466107",
       "owner":"191126281@N07",
       "secret":"06f861f8b0",
       "server":"65535",
       "farm":66,
       "title":"Prompt & Reliable Electrolux Oven Repairs in Gold Coast",
       "ispublic":1,
       "isfriend":0,
       "isfamily":0
    },
    {
       "id":"50970556873",
       "owner":"49965961@N00",
       "secret":"21f7a6524b",
       "server":"65535",
       "farm":66,
       "title":"IMG_20210222_145514",
       "ispublic":1,
       "isfriend":0,
       "isfamily":0
    }
]
"""

let data = json.data(using: .utf8)
let results: [Photo] = try! JSONDecoder().decode([Photo].self, from: data)
      
      



, [Photo] - Swift. : - !





, , .





" " Decodable , JSON.





  • JSON- - , . - , . , JSON- ! , -, Character , JSON- .





  • JSON - . ? , , : JSON (, ). ? , ( )





  • Decodable .





, . Decodable generic enum CodingKeys, () , JSON , , , ! , . , , : JSON- snake case , Swift camel case. ?





struct Photo: Decodable {
    let idInJSON: String
    let secretInJSON: String
    let serverInJSON: String
    
    enum CodingKeys: String, CodingKey {
        case idInJSON = "id_in_JSON"
        case secretInJSON = "secret_in_JSON"
        case serverInJSON = "server_in_JSON"
    }
}

      
      



rawValue CodingKeys , JSON-!





! JSON !





{
   "photos":{
      "page":1,
      "pages":"11824",
      "perpage":2,
      "total":"23648",
      "photo":[
         {
            "id":"50972466107",
            "owner":"191126281@N@7" ,
            "secret":"Q6f861f8b0",
            "server":"65535",
            "farm":66,
            "title":"Prompt & Reliable Electrolux Oven Repairs in Gold Coast",
            "ispublic":1,
            "isfriend":0,
            "isfamily":0
         },
         {
            "id":"50970556873",
            "owner":"49965961@NG0",
            "secret":"21f7a6424b",
            "server":"65535",
            "farm" 66,
            "title":"IMG_20210222_145514",
            "ispublic":1,
            "isfriend":0,
            "isfamily":0
         }
      ]
   },
   "stat":"ok"
}
      
      



:





  • , : "photos", "stat"





  • "photos" : "page", "pages", "perpages", "total", "photo"





  • "photo" - , .





?





  • . dummy . !





  • Decodable , CodingKeys! ! : Swift ( !) extension stored properties, computed properties odable/Encodable/Decodable, JSON .





, , : photos photo c





, !





// (1)   Photo      .
struct Photo: Decodable {
    let id: String
    let secret: String
    let server: String
}

// (2)  JSONContainer,          .
struct JSONContainer: Decodable {
    // (3) photos  c   "photos"  JSON,    ,        ,     - ,     photo!
    let photos: [Photo]
}

extension JSONContainer {
    // (4)  CodingKeys  .
    enum CodingKeys: String, CodingKey {
        case photos
        // (5)      ,       photos.
        // (6)    -  ,   PhotosKeys -    ,        photos
        enum PhotosKeys: String, CodingKey {
            // (7)      "photo"
            case photoKey = "photo"
        }
    }
    // (8)   
    init(from decoder: Decoder) throws {
        // (9)   JSON,      ,        - photos
        let container = try decoder.container(keyedBy: CodingKeys.self)
        // (10)    (nested - )  photos         
        let photosContainer = try container.nestedContainer(keyedBy: CodingKeys.PhotosKeys.self, forKey: .photos)
        // (11)    
        // (12)        photos -,       .photoKey (.photoKey.rawValue == "photo")
        photos = try photosContainer.decode([Photo].self, forKey: .photoKey)
    }
}
      
      



Itu saja, sekarang instance JSONDecoder. Akan memanggil decode () - di bawah tenda itu akan menggunakan penginisialisasi kami untuk menangani decoding





Kesimpulannya, saya ingin mengatakan bahwa bekerja dengan jaringan dalam pengembangan iOS dipenuhi dengan berbagai "kejutan", jadi jika Anda dapat menambahkan sesuatu di komentar - pastikan untuk melakukannya!





Terimakasih untuk semua!





PS Setelah beberapa waktu, disimpulkan bahwa tidak apa-apa untuk memetakan ke struktur akhir dalam kode hanya menggunakan perilaku built-in dari Codable. Kesimpulan dibuat setelah menonton sesi WWDC menganalisis pekerjaan dengan data yang diterima dari jaringan.





Tautan sesi








All Articles