Fui solicitado a escrever este artigo por um colapso quase nervoso causado pelo meu desejo de aprender como me comunicar com APIs de terceiros. Eu estava especificamente interessado no processo de decodificação de documentos JSON! Felizmente, evitei um colapso nervoso, então agora é hora de contribuir com a comunidade e tentar publicar meu primeiro artigo sobre Habré.
Por que existem problemas com uma tarefa tão simples?
Para entender de onde vêm os problemas, primeiro você precisa falar sobre o kit de ferramentas que usei. Para decodificar objetos JSON, usei o protocolo sintetizado relativamente novo da biblioteca Foundation - odable .
Codable é um protocolo integrado que permite codificar em um objeto de formato de texto e decodificar em um formato de texto. Codable é a soma de dois outros protocolos: Decodable e Encodable.
Vale ressaltar que um protocolo é denominado sintetizado quando alguns de seus métodos e propriedades possuem uma implementação padrão. Por que esses protocolos são necessários? Para tornar mais fácil trabalhar com a assinatura deles, pelo menos reduzindo a quantidade de código clichê.
Esses protocolos também permitem que você trabalhe com composição, não com herança!
Agora vamos falar sobre os problemas:
-, , 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)
}
}
É isso, agora que a instância do JSONDecoder. Chamará decode () - nos bastidores, usará nosso inicializador para lidar com a decodificação
Resumindo, gostaria de dizer que trabalhar com a rede no desenvolvimento do iOS é recheado de várias "surpresas", então se você puder acrescentar algo nos comentários - não deixe de fazê-lo!
Obrigado a todos!
PS Depois de algum tempo, concluiu-se que não há problema em mapear a estrutura final no código usando apenas o comportamento embutido de Codable. A conclusão foi feita após assistir à sessão do WWDC analisando o trabalho com os dados recebidos da rede.