Como decodificar JSON em Swift

Como decodificar JSON em Swift

Aprenda a decodificar JSON para um objeto na linguagem Swift

Contexto

Para quem está iniciando, talvez esteja se perguntando: porque preciso saber decodificar um conteúdo em JSON?

O formato JSON é amplamente utilizando em diversas linguagens para armazenar e transferir informações de maneira estruturada. Abaixo, alguns exemplos de cenários comuns:

  • Arquivo JSON que armazena de configurações

  • Consumir uma RESTFUL API que responda no formato JSON.

  • String contendo informações no formato JSON.

Neste artigos iremos ver como ler e decodificar o formato JSON usando a linguagem Swift.

Leitura do conteúdo JSON

O primeiro passo para ler um JSON em Swift é converter o seu conteúdo no formato Data, um tipo da linguagem Swift, que será usado para decodificar a informação em um tipo que iremos definir mais à frente. Abaixo, temos os exemplos de conversão para cada cenário:

String

Abaixo, temos um exemplo de uma string contendo uma lista de posts no formato JSON:

let jsonContent = """
[
  {
    "id":1,
    "title":"Como se tornar um programador",
    "content":"Para se tornar um programador, é necessário estudar linguagens de programação e desenvolver habilidades de resolução de problemas.",
    "created_at":"2023-04-18T14:00:00Z"
  },
  {
    "id":2,
    "title":"Os benefícios da meditação",
    "content":"A meditação pode ajudar a reduzir o estresse, melhorar a qualidade do sono e aumentar a sensação de bem-estar.",
    "created_at":"2023-04-18T14:00:00Z"
  },
  {
    "id":3,
    "title":"Receita de bolo de chocolate",
    "content":"Para fazer um delicioso bolo de chocolate, você vai precisar de farinha de trigo, açúcar, ovos, óleo, cacau em pó e fermento em pó.",
    "created_at":"2023-04-18T14:00:00Z"
  }
]
"""

Dessa forma, usando as 3 aspas("""), podemos declarar uma string com múltiplas linhas. Agora, podemos fazer a conversão da nossa string para o formato Data:

// Usamos o .utf8 para converter a string
// para um padrão que permita acentuação
let data = Data(jsonContent.utf8)

Arquivo

Supondo que tenhamos um arquivo .json que contenha uma lista de artigos:

[
  {
    "id":1,
    "title":"Como se tornar um programador",
    "content":"Para se tornar um programador, é necessário estudar linguagens de programação e desenvolver habilidades de resolução de problemas.",
    "created_at":"2023-04-18T14:00:00Z"
  },
  {
    "id":2,
    "title":"Os benefícios da meditação",
    "content":"A meditação pode ajudar a reduzir o estresse, melhorar a qualidade do sono e aumentar a sensação de bem-estar.",
    "created_at":"2023-04-18T14:00:00Z"
  },
  {
    "id":3,
    "title":"Receita de bolo de chocolate",
    "content":"Para fazer um delicioso bolo de chocolate, você vai precisar de farinha de trigo, açúcar, ovos, óleo, cacau em pó e fermento em pó.",
    "created_at":"2023-04-18T14:00:00Z"
  }
]

Considerando que o nome do nosso arquivo é artigos.json, ele pode ser lido da seguinte forma:

// Caminho para um arquivo
let filePath = Bundle.main.path(forResource: "artigos", ofType: "json")

// Transforma o conteúdo do arquivo em Data
let data = try? Data(contentsOf: filePath)

Usamos o try? antes do inicializador do Data porque a conversão do nosso arquivo para Data pode falhar e causar uma exceção. Para não termos que lidar com exceções, usamos o try?, que irá fazer com que o Data(contentsOf:) retorne nil caso um erro ocorra.

Requisição em API

Para fazer uma requisição em uma API, usaremos o método mais tradicional, URLSession.shared.dataTask(for:). Considerando uma URL fictícia que poderia nos fornecer a lista de artigos que desejamos obter, temos o seguinte código:

URLSession.shared.dataTask(with: url) { (data, response, error) in
    // Verificamos se um objeto data recebido não é nulo
    guard let data else { return }

    // A partir daqui, executamos o passo de decodificação
    // ...
}.resume()

Criação do tipo em Swift

Precisamos definir a estrutura dos objetos contida no nosso JSON, para qual os dados serão decodificados. Para isso, vamos criar um struct que conforme com o protocolo Decodable. Esse protocolo está disponível no framework Foundation e é usado para definição de objetos que sejam decodificáveis. A seguir, temos a declaração do nosso tipo Post:

struct Post: Decodable {
    let id: Int
    let title: String
    let content: String
    let createdAt: String

    /* 
    *  Definição dos atributos que possuem
    *  uma chave num formato diferente no JSON
    *  ex.: Nomes em snake_case
    */
    enum CodingKeys: String, CodingKey {
        case id, title, content
        case createdAt = "created_at"
    }
}

Cada atributo do objeto contido no nosso JSON é definido como uma propriedade do nosso struct. Através do enum CodingKeys podemos definir uma associação entre chaves a serem "renomeadas" do JSON para atributos do nosso objeto. Acima, por exemplo, convertemos o created_at(em snake-case) para createdAt(em camel-case). Esse mesmo comportamento pode ser obtido atribuindo à propriedade keyDecodingStrategy do JSONDecoder o valor .convertFromSnakeCase, que será visto no próximo tópico. O uso dessa propriedade elimina a necessidade de definirmos as associações para cada propriedade que esteja no formato snake-case.

Com nosso struct definido, poderemos fazer a decodificações de outras estruturas para ele, como um JSON, que é o nosso objetivo.

Decodificação

Agora que já temos um objeto Data e um tipo definido podemos usá-los para decodificar nosso conteúdo JSON. Para isso, o framework Foundation fornece uma classe JSONDecoder criada exatamente com esse objetivo. Abaixo, temos um exemplo do processo de decodificação:

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let artigos = try? decoder.decode([Post].self, from: data)

print(artigos)

O método decode da classe JSONDecoder recebe dois parâmetros. O primeiro é o tipo do objeto a ser decodificado e o segundo é o objeto Data que contém os dados obtidos anteriormente.

No exemplo deste artigo, o tipo a ser decodificado é uma lista do tipo Post. O self é usado para obter o tipo esperado, que é [Post](um array de Post).

Vale ressaltar que o Data esperado não pode ser nulo, então certifique-se de que seu objeto data não é nil antes de passá-lo para o decode.

Conclusão

Conhecer essas 3 formas básicas de decodificar um JSON em Swift já te dá uma base suficiente para aplicar o conceito em diversos contextos. Abaixo, a documentação de alguns dos recursos utilizados nesse artigo:

Gostou deste artigo? Compartilhe e me siga para mais conteúdos como esse!

Capa por Ferenc Almasi na Unsplash

Did you find this article valuable?

Support Matheus dos Reis de Jesus by becoming a sponsor. Any amount is appreciated!