Arbeiten mit komplexen JSON-Objekten in Swift (codierbar)

Ich wurde aufgefordert, diesen Artikel zu schreiben, da ich fast nervös werden wollte, weil ich lernen wollte, wie man mit APIs von Drittanbietern kommuniziert. Ich war speziell an der Dekodierung von JSON-Dokumenten interessiert! Glücklicherweise habe ich einen Nervenzusammenbruch vermieden. Jetzt ist es an der Zeit, einen Beitrag zur Community zu leisten und meinen ersten Artikel über Habré zu veröffentlichen.





Warum gibt es überhaupt Probleme mit einer so einfachen Aufgabe?





Um zu verstehen, woher die Probleme kommen, müssen Sie zuerst über das von mir verwendete Toolkit sprechen. Zum Dekodieren von JSON-Objekten habe ich das relativ neue synthetisierte Protokoll der Foundation-Bibliothek verwendet - odable .





Codable ist ein integriertes Protokoll, mit dem Sie in ein Textformatobjekt codieren und aus einem Textformat decodieren können. Codierbar ist die Summe von zwei anderen Protokollen: Decodierbar und Codierbar.





Es lohnt sich zu reservieren, dass ein Protokoll als synthetisiert bezeichnet wird, wenn einige seiner Methoden und Eigenschaften eine Standardimplementierung haben. Warum werden solche Protokolle benötigt? Um die Arbeit mit dem Signieren zu vereinfachen, zumindest durch Reduzieren der Menge an Boilerplate-Code.





Mit diesen Protokollen können Sie auch mit Komposition und nicht mit Vererbung arbeiten!





Lassen Sie uns nun über die Probleme sprechen:





  • -, , 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)
    }
}
      
      



Das war's, jetzt, wo die Instanz des JSONDecoders. Ruft decode () auf - unter der Haube wird unser Initialisierer verwendet, um die Decodierung zu erledigen





Zusammenfassend möchte ich sagen, dass die Arbeit mit dem Netzwerk in der iOS-Entwicklung mit verschiedenen "Überraschungen" verbunden ist. Wenn Sie also etwas in den Kommentaren hinzufügen können, tun Sie es auf jeden Fall!





Danke an alle!





PS Nach einiger Zeit wurde der Schluss gezogen, dass es in Ordnung ist, die endgültige Struktur im Code nur mit dem integrierten Verhalten von Codable abzubilden. Die Schlussfolgerung wurde gezogen, nachdem die WWDC-Sitzung die Arbeit mit den vom Netzwerk empfangenen Daten analysiert hatte.





Sitzungslink








All Articles