diegoRodicio

Está documentación está a túa disposición sin ningún custo económico. Sen embargo, para a súa elaboración dedico moito tempo e recursos, polo que agradecería unha colaboración co que consideres oportuno. Gracias.

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 e Identifiable, para poder cargar o JSON e empregar a vista de List.
  • 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
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 nova
    • url: A dirección url da nova
    • title: O título da nova
    • image: A dirección url da imaxe de portada da nova
    • summary: 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 nova
    • url: A dirección url da nova
    • title: O título da nova
    • image: A dirección url da imaxe de portada da nova
    • summary: 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 protocolos Codable e Identifiable, para poder cargar o JSON e empregar a vista de List.
  • 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 chave title do JSON
  • A propiedades descricion se corresponde coa chave summary do JSON
  • A propiedades imaxe se corresponde coa chave image 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]?

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

Share Your Valuable Opinions