Cargar un arquivo JSON #
Neste apartado veremos unha forma de cargar un arquivo JSON e traducir as entradas nun array de obxectos.
- Necesitamos unha clase que teña, como propiedades, as que se correspondan coas claves do arquivo JSON que desexamos empregar.
- Unha función que cargue o arquivo JSON nun array de obxectos da clase anterior.
Para explicar o anterior, supoñamos que temos o seguinte arquivo JSON:
[
{
"id": "aa32jj887hhg55",
"nome": "Tesla Model 3",
"descricion": "Coche totalmente eléctrico de 4 portas. Autonomía 500 km. 0-100 km/h 3.2 segundos.",
"hibrido": false,
"nomeImaxe": "tesla_model_3"
},
{
"id": "bb32jj887hhg77",
"nome": "Tesla Model S",
"descricion": "Coche eléctrico de 5 portas. O modelo Performance ten varios motores eléctrico que acadan 1020 CV. 0-100 km/h 2.1 segundos.",
"hibrido": false,
"nomeImaxe": "tesla_model_s"
}
]
1 de 3: Creación do modelo de datos: DataModel #
Necesitamos unha ou varias estruturas que teñan, como propiedades, as que se correspondan coas chaves do arquivo JSON.
- A estrutura CocheDataModel que vamos a crear, ten que implementar os protocolos
Codable
eIdentifiable
, para poder cargar o JSON e empregar a vista deList
. - Dentro da estrutura definimos unha enumeración
enum CodingKeys: String, CodingKey
(sempre ten que ter ese nome), de xeito que cada caso da enumeración se corresponderá cunha propiedade da estrutura, e, si o nome desa propiedade non coincide coa chave do JSON, se iguala ao nome da chave.- Así, no exemplo, a propiedade
imaxe
non coincide co nome da chave no JSON (nomeImaxe
) - O nome do resto de propiedades si coincide co nome das chaves no JSON
- Así, no exemplo, a propiedade
struct CocheDataModel: Codable, Identifiable {
var id: String
var nome: String
var descricion: String
var hibrido: Bool
var imaxe: String
enum CodingKeys: String, CodingKey {
case id
case nome
case descricion
case hibrido
case imaxe = "nomeImaxe"
}
}
2 de 3: Cargar JSON #
Empregaremos unha función de que lea un arquivo JSON (pasándolle o nome do arquivo) e devolve un array de obxectos que teña as propiedades que se correspondan coas claves do arquivo JSON.
import Foundation
func cargarJSON<TipoEstrutura: Decodable>(nomeArquivoJSON: String) -> TipoEstrutura {
let datos: Data
guard let arquivo = Bundle.main.url(forResource: nomeArquivoJSON,
withExtension: nil)
else {
fatalError("\(nomeArquivoJSON) non atopado no proxecto.")
}
do {
datos = try Data(contentsOf: arquivo)
} catch {
fatalError("Non foi posible cargar o arquivo \(nomeArquivoJSON): \(error)")
}
do {
return try JSONDecoder().decode(TipoEstrutura.self, from: datos)
} catch {
fatalError("Non foi posible parsear o arquivo \(nomeArquivoJSON): \(error)")
}
}
A función cargarJSON
é un exemplo estándar de como cargar un arquivo JSON e pódese utilizar en calquera outro proxecto, é dicir, poderemos empregar esta función para cargar calquera JSON, non so o noso.
NOTA: No noso exemplo, cando empreguemos a función, devolveranos un array de obxectos CocheDataModel
.
3 de 3: Obter e codificar os datos do JSON #
Por último, empregamos a función cargarJSON
(Pasándolle o nome do arquivo JSON) para decodificar o JSON e, posteriormente vamos a almacenar os datos cargados no modelo creado no primeiro paso.
Para decodificar e almacenar os datos, utilizaremos unha final class
que implementa o protocolo ObservableObject
.
Dentro da clase, teremos unha propiedade onde almacenaremos os datos (empregando unha estrutura creada no modelo de datos) que imos a empregar no noso deseño de interfaz mediante SwiftUI. É por iso que necesitamos implementar o protocolo ObservableObject
e imos empregar o property wrapper @Published
.
Desta maneira, cada vez que se actualice esta propiedade, a vista que utilizaremos en SwifUI se redebuxará coas novas descargadas.
No caso do exemplo, a clase a chamaremos AlmacenCoches
. Como comentei:
- Teremos unha propiedade
Published
onde almacenaremos os datos empregando o modelo de datos programado no primeiro punto. - Queremos almacenar un array de coches, polo tanto, no caso do exemplo, a propiedade será:
@Published var coches: [CocheDataModel]
Así, no exemplo anterior (tendo en conta que o arquivo JSON está almacenado noso proxecto co nome datosCoches.json
):
var coches: [CocheDataModel]
coches = cargarJSON(nomeArquivoJSON: "datosCoches.json")
Cargar JSON desde unha URL #
A continuación veremos como cargar un arquivo JSON dende unha URL e, posteriormente, tal e como faciamos no punto anterior, transformar o JSON descargado a un modelo da nosa app.
Para iso empregaremos URLSession
e JSONDecoder
.
Para realizar esta tarefa suporemos un caso real. A través da url https://www.iessanmamede.com/category/novas/feed/json obteremos todas as novas da web do IES San Mamede en formato JSON.
Se descargamos este arquivo no navegador, observamos que o JSON ten o seguinte formato:
{
"version": "https://jsonfeed.org/version/1.1",
"user_comment": "This feed allows you to read the posts from this site in any feed reader that supports the JSON Feed format....",
"next_url": "https://www.iessanmamede.com/category/novas/feed/json?paged=2",
"home_page_url": "https://www.iessanmamede.com/category/novas",
"feed_url": "https://www.iessanmamede.com/category/novas/feed/json",
"language": "gl-ES",
"title": "Novas arquivos - IES San Mamede",
"description": "P\u00e1xina Oficial do Instituto de Educaci\u00f3n Secundaria IES San Mamede (Maceda) Ourense",
"icon": "https://www.iessanmamede.com/wp-content/uploads/2023/08/logo_sm_150.jpg",
"items": [
{
"id": "https://www.iessanmamede.com/?p=2159",
"url": "https://www.iessanmamede.com/proxecto-emprende-sorteo-ipad/",
"title": "Proxecto eMprende \u2013 Sorteo iPad",
"content_html": "Contido da nova en html",
"date_published": "2024-01-24T16:27:12+01:00",
"date_modified": "2024-01-24T18:30:40+01:00",
"authors": [
{
"name": "drodicio",
"url": "https://www.iessanmamede.com/author/drodicio/",
"avatar": "https://secure.gravatar.com/avatar/e6301037117d1756e160e4853dd3060e?s=512&d=mm&r=g"
}
],
"author": {
"name": "drodicio",
"url": "https://www.iessanmamede.com/author/drodicio/",
"avatar": "https://secure.gravatar.com/avatar/e6301037117d1756e160e4853dd3060e?s=512&d=mm&r=g"
},
"image": "https://www.iessanmamede.com/wp-content/uploads/2024/01/IMG_0403_modificado-scaled.jpg",
"tags": [
"Ultimas Novas"
],
"summary": "No marco do proxecto eMprende, a semana pasada tivo lugar o proceso de entrega dun iPad ao alumno agraciado no sorteo, Alejandro Gil..."
},
{
"id": "https://www.iessanmamede.com/?p=1957",
"url": "https://www.iessanmamede.com/java-magician/",
"title": "Java Magician",
"content_html": "Contido da nova en html",
"date_published": "2023-11-06T12:06:53+01:00",
"date_modified": "2023-11-06T12:19:45+01:00",
"authors": [
{
"name": "drodicio",
"url": "https://www.iessanmamede.com/author/drodicio/",
"avatar": "https://secure.gravatar.com/avatar/e6301037117d1756e160e4853dd3060e?s=512&d=mm&r=g"
}
],
"author": {
"name": "drodicio",
"url": "https://www.iessanmamede.com/author/drodicio/",
"avatar": "https://secure.gravatar.com/avatar/e6301037117d1756e160e4853dd3060e?s=512&d=mm&r=g"
},
"image": "https://www.iessanmamede.com/wp-content/uploads/2023/11/java-scaled.webp",
"tags": [
"dam",
"fp",
"Inform\u00e1tica",
"java",
"programaci\u00f3n"
],
"summary": "En Java Magician podes atopar diversos tutoriais de programaci\u00f3n en Java."
}
]
}
Vamos a quitar do JSON aqueles datos que non vamos a empregar na aplicación, de xeito que so nos interesará esta parte:
{
...
"items": [
{
"id": "https://www.iessanmamede.com/?p=2159",
"url": "https://www.iessanmamede.com/proxecto-emprende-sorteo-ipad/",
"title": "Proxecto eMprende \u2013 Sorteo iPad",
"image": "https://www.iessanmamede.com/wp-content/uploads/2024/01/IMG_0403_modificado-scaled.jpg",
"summary": "No marco do proxecto eMprende, a semana pasada tivo lugar o proceso de entrega dun iPad ao alumno agraciado no sorteo, Alejandro Gil..."
},
{
...
},
{
...
}
]
...
}
Os datos que nos interesan son:
- A chave
items
, de xeito que será un array, onde cada elemento do array será unha nova da que teremos as seguintes chaves:id
: O identificador da novaurl
: A dirección url da novatitle
: O título da novaimage
: A dirección url da imaxe de portada da novasummary
: O resumo da nova
1 de 3: Creación do modelo de datos: DataModel #
Neste punto teremos que programar o modelo de datos que me permitirá almacenar e xestionar os datos procedentes do JSON. Tal e como explico no punto anterior, necesitaremos crear as estruturas necesarias para procesar:
- A chave
items
, de xeito que será un array, onde cada elemento do array será unha nova da que teremos as seguintes chaves:id
: O identificador da novaurl
: A dirección url da novatitle
: O título da novaimage
: A dirección url da imaxe de portada da novasummary
: O resumo da nova
Ao igual que fixemos no apartado de cargar un arquivo JSON, necesitamos as clases/estruturas que teñan, como propiedades, as que se correspondan coas chaves do arquivo JSON. Nesta ocasión, necesitaremos crear dúas clases
- A clase correspondente coa chave
items
, que tal e como menciono será un array de novas. - A clase que se corresponde con unha nova
1.1: Creación do modelo de datos para unha nova (NovaDataModel) #
- A estrutura
NovaDataModel
que vamos a crear, ten que implementar os protocolosCodable
eIdentifiable
, para poder cargar o JSON e empregar a vista deList
. - As propiedades da estrutura que vamos a crear coinciden coas chaves do arquivo JSON que nos interesan. Non é obrigatorio incluír como propiedades todas as chaves do JSON.
Dentro da estrutura definimos unha enumeración enum CodingKeys: String, CodingKey
(sempre ten que ter ese nome), de xeito que cada caso da enumeración se corresponderá con unha propiedade da estrutura, e, si o nome desa propiedade non coincide coa chave do JSON, se iguala ao nome da chave.
Así, teremos, por exemplo:
struct NovaDataModel: Decodable, Identifiable {
var id: String
var url: String
var titulo: String
var descricion: String
let imaxe: String
enum CodingKeys: String, CodingKey {
case id
case url
case titulo = "title"
case descricion = "summary"
case imaxe = "image"
}
}
No exemplo vemos que:
- A propiedades
titulo
se corresponde coa chavetitle
do JSON - A propiedades
descricion
se corresponde coa chavesummary
do JSON - A propiedades
imaxe
se corresponde coa chaveimage
do JSON - O resto de propiedades coinciden en nome coas chaves do JSON
1.1: Creación do modelo de datos para os items -array de Novas- (NovasDataModel) #
Como comentamos máis arriba, as novas do IES San Mamede están dentro da chave items
do JSON, de xeito que cada elemento de dita chave (array) será unha nova.
O que temos que facer é obter un array de NovaDataModel
, de xeito que debemos iterar a partir da chave items
. Do mesmo xeito que no paso anterior, creamos a estrutura:
struct NovasDataModel: Decodable {
let novas: [NovaDataModel]
enum CodingKeys: String, CodingKey {
case novas = "items"
}
}
2 de 3: Obter e codificar os datos do JSON #
O seguinte paso e obter o arquivo JSON, decodificalo e almacenar os datos no modelo creado no primeiro paso.
Para decodificar e almacenar os datos, utilizaremos unha final class
que implementa o protocolo ObservableObject
.
Dentro da clase, teremos unha propiedade onde almacenaremos os datos (empregando unha estrutura creada no modelo de datos) que imos a empregar no noso deseño de interfaz mediante SwiftUI. É por iso que necesitamos implementar o protocolo ObservableObject
e imos empregar o property wrapper @Published
.
Desta maneira, cada vez que se actualice esta propiedade, a vista que utilizaremos en SwifUI se redebuxará coas novas descargadas.
No caso do exemplo, a clase a chamaremos Novas
. Como comentei:
- Teremos unha propiedade
Published
onde almacenaremos os datos empregando o modelo de datos programado no primeiro punto. - Queremos almacenar un array de novas, polo tanto, no caso do exemplo, a propiedade será:
@Published var novas: [NovaDataModel]
- Neste caso, ao ser un array, a propiedade a podemos inicializar:
@Published var novas: [NovaDataModel] = []
- Se a propiedade non fose un array, seguramente non a poderemos inicializar (xa que non creamos o construtor para as estruturas do modelo de datos). Neste caso, a mellor opción sería empregar opcionais:
@Published var novas: [NovaDataModel]?
- Neste caso, ao ser un array, a propiedade a podemos inicializar:
Finalmente, dentro da clase Novas
, imos crear un método chamado getNovas()
que o que fará será unha petición HTTP á páxina do IES San Mamede que nos devolve o arquivo JSON coas novas.
Para iso debemos especificar a URL e usar URLSession
para realizar a petición HTTP. O código quedaría do seguinte xeito:
final class Novas: ObservableObject {
@Published var novas: [NovaDataModel] = []
let url = URL(string: "https://www.iessanmamede.com/category/novas/feed/json")
func getNovas() {
URLSession.shared.dataTask(with: url!) { datos, resposta, error in
if let _ = error {
print("Error")
}
if let datos = datos,
let respostaHTTP = resposta as? HTTPURLResponse,
respostaHTTP.statusCode == 200 {
let novas = try! JSONDecoder().decode(NovasDataModel.self, from: datos)
print("Novas \(novas)")
DispatchQueue.main.async {
self.novas = novas.novas
}
}
}.resume()
}
}
3 de 3: Implementar a vista en SwifUI #
Agora xa podemos procesar as novas obtidas e almacenadas na clase Novas
mediante unha interfaz en SwiftUI.
Velaquí un exemplo:
struct NovasView: View {
@StateObject var novas: Novas = Novas()
var body: some View {
NavigationView {
List(novas.novas) {
nova in
Link(destination: URL(string: nova.url)!) {
HStack {
ImaxeNovaView(url: nova.imaxe)
VStack {
TituloNovaView(titulo: nova.titulo)
ResumoNovaView(resumo: nova.descricion)
}
}
}
}
.navigationTitle("Novas IES San Mamede")
}.onAppear {
novas.getNovas()
}
}
}
struct ImaxeNovaView: View {
var url: String
var body: some View {
AsyncImage(url: URL(string: url)) { image in
image
.resizable()
.scaledToFit()
.frame(width: 50)
.cornerRadius(5)
.padding()
} placeholder: {
ProgressView()
}
}
}
struct TituloNovaView: View {
var titulo: String
var body: some View {
Text(titulo).font(/*@START_MENU_TOKEN@*/.title/*@END_MENU_TOKEN@*/)
}
}
struct ResumoNovaView: View {
var resumo: String
var body: some View {
Text(resumo).font(.subheadline)
}
}