Decodificación de arquivos JSON #
Procesar un arquivo JSON en Swift implica cargar o JSON, definir a estrutura de datos compatible, decodificar os datos e usalos na aplicación.
Vou explicar cada paso con detalle e proporcionar un exemplo práctico.
Pasos para procesar un arquivo JSON en Swift #
- Definir a estrutura de datos en Swift: Para decodificar JSON, necesitamos unha clase que cumpra co protocolo
Decodable
. Os nomes das propiedades deben coincidir cos do JSON, pero podemos renomealos usandoCodingKeys
. - Obter os datos JSON: O JSON pode vir de distintas fontes:
- Desde un arquivo local no proxecto (
Bundle.main.url(forResource:)
). - Desde unha URL remota (
URLSession
). - Desde un String na aplicación (
data(using: .utf8)
). Este apartado non o vamos a ver.
- Desde un arquivo local no proxecto (
- Decodificar o JSON: Usamos
JSONDecoder()
para converter os datos en obxectos Swift.
UtilidadesJSON.swift #
Todas as funcións que se empregan neste apartado están dentro deste arquivo de utilidades:
//
// UtilidadesJSON.swift
// CargarJSONdesdeURL-Novas-IES
//
// Created by Diego Rodicio Álvarez on 18/2/25.
//
import Foundation
/// Abre un arquivo do proxecto e carga os seus datos.
///
/// Esta función busca un arquivo dentro do `Bundle.main` utilizando o seu nome e extensión.
/// Se o arquivo existe, carga o seu contido como `Data`. En caso contrario, detén a execución con `fatalError()`.
///
/// - Parameter nomeArquivo: Nome do arquivo dentro do proxecto (sen extensión).
/// - Returns: Os datos do arquivo en formato `Data`, ou `nil` se a carga falla.
/// - Warning: Esta función usa `fatalError()`, polo que a aplicación fallará se o arquivo non se atopa ou non se pode cargar.
///
/// - Example:
/// ```swift
/// if let datos = abrirArquivo(nomeArquivo: "datos.json") {
/// print("Arquivo cargado con éxito")
/// }
/// ```
func abrirArquivo(nomeArquivo: String) -> Data? {
var datos: Data
// Busca o arquivo dentro do bundle da aplicación
guard let arquivo = Bundle.main.url(forResource: nomeArquivo,
withExtension: nil)
else {
fatalError("\(nomeArquivo) non atopado no proxecto.")
}
do {
// Intenta cargar os datos do arquivo
datos = try Data(contentsOf: arquivo)
} catch {
fatalError("Non foi posible cargar o arquivo \(nomeArquivo): \(error)")
}
return datos
}
/// Descarga e carga os datos dun arquivo desde unha URL.
///
/// Esta función intenta descargar os datos dun arquivo situado nunha URL remota ou local.
/// Se a descarga é exitosa, devolve os datos en formato `Data`. En caso de erro, imprime unha mensaxe e devolve `nil`.
///
/// - Parameter url: A URL do arquivo que se desexa descargar.
/// - Returns: Os datos do arquivo en formato `Data`, ou `nil` se a descarga falla.
///
/// - Example:
/// ```swift
/// if let url = URL(string: "https://exemplo.com/arquivo.json"),
/// let datos = abrirArquivo(url: url) {
/// print("Arquivo descargado con éxito, tamaño: \(datos.count) bytes")
/// }
/// ```
func abrirArquivo(url: URL) -> Data? {
do {
// Intenta descargar e cargar os datos do arquivo na URL fornecida
let data = try Data(contentsOf: url)
return data
} catch {
// Se ocorre un erro, imprímeo e devolve `nil`
print("Erro ao descargar o arquivo: \(error)")
return nil
}
}
/// Decodifica un arquivo JSON nunha estrutura específica.
///
/// Esta función toma datos en formato `Data` e intenta decodificalos nunha estrutura que cumpra o protocolo `Decodable`.
/// Se a decodificación falla, a execución deterase con `fatalError()`.
///
/// - Parameter arquivo: Os datos en formato `Data` que conteñen o JSON.
/// - Returns: Unha instancia de `TipoEstrutura`, que é un tipo xenérico que implementa `Decodable`.
/// - Throws: Non lanza erros explicitamente, pero usa `fatalError()` en caso de fallo.
/// - Warning: Se hai un erro ao procesar o JSON, a aplicación deterase.
/// - Note: Esta función só funciona con tipos que cumpren `Decodable`.
///
/// - Example:
/// ```swift
/// struct Usuario: Decodable {
/// let nome: String
/// let idade: Int
/// }
///
/// let jsonData = """
/// {
/// "nome": "Diego",
/// "idade": 46
/// }
/// """.data(using: .utf8)!
///
/// let usuario: Usuario = procesarJSON(arquivo: jsonData)
/// print(usuario.nome) // "Diego"
/// ```
/*
📌 Que é TipoEstrutura: Decodable?
TipoEstrutura é un parámetro de tipo xenérico. Significa que esta función pode traballar con calquera tipo de datos, sempre que cumpra cunha determinada restrición.
• TipoEstrutura → É un tipo de datos que será determinado cando se chame á función.
• : Decodable → Indica que TipoEstrutura debe cumprir o protocolo Decodable. É dicir, debe ser un tipo que poida ser decodificado desde JSON.
📌 Resumo
✅ <TipoEstrutura: Decodable> fai que a función sexa xerérica, podendo aceptar calquera tipo que cumpra Decodable.
✅ Permite reutilizar a mesma función para diferentes estruturas de datos sen escribir código duplicado.
✅ Evita o uso de tipos específicos e fai que o código sexa máis flexible e escalable. 🚀
*/
func procesarJSON<TipoEstrutura: Decodable>(arquivo: Data) -> TipoEstrutura {
do {
// Intenta decodificar os datos JSON na estrutura fornecida
return try JSONDecoder().decode(TipoEstrutura.self, from: arquivo)
} catch {
// Se ocorre un erro na decodificación, a aplicación deterase
fatalError("Non foi posible parsear o arquivo: \(error)")
}
}
Exemplo: Procesar un JSON dun arquivo local ou URL #
Exemplo de JSON (usuarios.json) #
{
"usuarios": [
{
"id": 1,
"nome": "Ana",
"idade": 30,
"correo": "ana@example.com"
},
{
"id": 2,
"nome": "Carlos",
"idade": 25,
"correo": "carlos@example.com"
}
]
}
Paso 1: Crear a estrutura de datos en Swift #
Definimos as estruturas que se correspondan co JSON. Empregamos:
Decodable
permite que JSONDecoder converta o JSON en estas estruturas.Identifiable
permite que SwiftUI os use en vistas de listas (List
).- Os
CodingKeys
permiten cambiar os nomes das propiedades para que coincidan co JSON. Se o JSON cambia os nomes das claves, só hai que modificarCodingKeys
sen tocar o resto do código.
import Foundation
// Estrutura para un usuario individual
struct UsuarioDataModel: Decodable, Identifiable {
let id: Int
let nome: String
let idade: Int
let email: String
// Mapeo de chaves JSON -> Propiedades da clase
enum CodingKeys: String, CodingKey {
case id
case nome
case idade
case email = "correo" // O JSON usa "correo", pero na clase de Swift usamos "email"
}
}
// Estrutura para o conxunto de usuarios
struct UsuariosDataModel: Decodable {
let items: [UsuarioDataModel]
// Mapeo de chaves JSON -> Propiedades da clase
enum CodingKeys: String, CodingKey {
case items = "usuarios"
}
}
Paso 2a: Función para abrir o arquivo JSON no caso de que estea dentro do proxecto #
Se o JSON está dentro do proxecto, cargámolo con Bundle.main.url(forResource:)
/// Abre un arquivo do proxecto e carga os seus datos.
///
/// Esta función busca un arquivo dentro do `Bundle.main` utilizando o seu nome e extensión.
/// Se o arquivo existe, carga o seu contido como `Data`. En caso contrario, detén a execución con `fatalError()`.
///
/// - Parameter nomeArquivo: Nome do arquivo dentro do proxecto (sen extensión).
/// - Returns: Os datos do arquivo en formato `Data`, ou `nil` se a carga falla.
/// - Warning: Esta función usa `fatalError()`, polo que a aplicación fallará se o arquivo non se atopa ou non se pode cargar.
///
/// - Example:
/// ```swift
/// if let datos = abrirArquivo(nomeArquivo: "datos.json") {
/// print("Arquivo cargado con éxito")
/// }
/// ```
func abrirArquivo(nomeArquivo: String) -> Data? {
var datos: Data
// Busca o arquivo dentro do bundle da aplicación
guard let arquivo = Bundle.main.url(forResource: nomeArquivo,
withExtension: nil)
else {
fatalError("\(nomeArquivo) non atopado no proxecto.")
}
do {
// Intenta cargar os datos do arquivo
datos = try Data(contentsOf: arquivo)
} catch {
fatalError("Non foi posible cargar o arquivo \(nomeArquivo): \(error)")
}
return datos
}
Paso 2b: Función para abrir o arquivo JSON a partir dunha URL #
/// Descarga e carga os datos dun arquivo desde unha URL.
///
/// Esta función intenta descargar os datos dun arquivo situado nunha URL remota ou local.
/// Se a descarga é exitosa, devolve os datos en formato `Data`. En caso de erro, imprime unha mensaxe e devolve `nil`.
///
/// - Parameter url: A URL do arquivo que se desexa descargar.
/// - Returns: Os datos do arquivo en formato `Data`, ou `nil` se a descarga falla.
///
/// - Example:
/// ```swift
/// if let url = URL(string: "https://exemplo.com/arquivo.json"),
/// let datos = abrirArquivo(url: url) {
/// print("Arquivo descargado con éxito, tamaño: \(datos.count) bytes")
/// }
/// ```
func abrirArquivo(url: URL) -> Data? {
do {
// Intenta descargar e cargar os datos do arquivo na URL fornecida
let data = try Data(contentsOf: url)
return data
} catch {
// Se ocorre un erro, imprímeo e devolve `nil`
print("Erro ao descargar o arquivo: \(error)")
return nil
}
}
Paso 3: Función para decodificar o JSON #
/// Decodifica un arquivo JSON nunha estrutura específica.
///
/// Esta función toma datos en formato `Data` e intenta decodificalos nunha estrutura que cumpra o protocolo `Decodable`.
/// Se a decodificación falla, a execución deterase con `fatalError()`.
///
/// - Parameter arquivo: Os datos en formato `Data` que conteñen o JSON.
/// - Returns: Unha instancia de `TipoEstrutura`, que é un tipo xenérico que implementa `Decodable`.
/// - Throws: Non lanza erros explicitamente, pero usa `fatalError()` en caso de fallo.
/// - Warning: Se hai un erro ao procesar o JSON, a aplicación deterase.
/// - Note: Esta función só funciona con tipos que cumpren `Decodable`.
///
/// - Example:
/// ```swift
/// struct Usuario: Decodable {
/// let nome: String
/// let idade: Int
/// }
///
/// let jsonData = """
/// {
/// "nome": "Diego",
/// "idade": 46
/// }
/// """.data(using: .utf8)!
///
/// let usuario: Usuario = procesarJSON(arquivo: jsonData)
/// print(usuario.nome) // "Diego"
/// ```
/*
📌 Que é TipoEstrutura: Decodable?
TipoEstrutura é un parámetro de tipo xenérico. Significa que esta función pode traballar con calquera tipo de datos, sempre que cumpra cunha determinada restrición.
• TipoEstrutura → É un tipo de datos que será determinado cando se chame á función.
• : Decodable → Indica que TipoEstrutura debe cumprir o protocolo Decodable. É dicir, debe ser un tipo que poida ser decodificado desde JSON.
📌 Resumo
✅ <TipoEstrutura: Decodable> fai que a función sexa xerérica, podendo aceptar calquera tipo que cumpra Decodable.
✅ Permite reutilizar a mesma función para diferentes estruturas de datos sen escribir código duplicado.
✅ Evita o uso de tipos específicos e fai que o código sexa máis flexible e escalable. 🚀
*/
func procesarJSON<TipoEstrutura: Decodable>(arquivo: Data) -> TipoEstrutura {
do {
// Intenta decodificar os datos JSON na estrutura fornecida
return try JSONDecoder().decode(TipoEstrutura.self, from: arquivo)
} catch {
// Se ocorre un erro na decodificación, a aplicación deterase
fatalError("Non foi posible parsear o arquivo: \(error)")
}
}
Paso 4: Usar as funcións anteriores #
if let datos = abrirArquivo(nomeArquivo: "usuarios") {
if let usuarios: UsuariosDataModel = procesarJSON(arquivo: datos) {
for usuario in usuarios.items {
print("Usuario: \(usuario.nome), Email: \(usuario.email)")
}
}
}
Exemplo cargar un arquivo JSON #
Neste apartado veremos outro exemplo para cargar un arquivo JSON e traducir as entradas nun array de obxectos.
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"
}
]

Paso 1 de 5: Crear a estrutura de datos #
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
Así, supondo o JSON do exemplo, teremos o seguinte código onde implementamos modelo de datos na clase CocheDataModel
:
struct CocheDataModel: Codable, Identifiable {
var id: String
var nome: String
var descricion: String
var hibrido: Bool
var imaxe: String
// Mapeo de chaves JSON -> Propiedades da clase
enum CodingKeys: String, CodingKey {
case id
case nome
case descricion
case hibrido
case imaxe = "nomeImaxe" // O JSON usa "nomeImaxe", pero na clase de Swift usamos "imaxe"
}
}

Paso 2 de 5: Abrir o arquivo JSON #
Empregaremos unha función de que lea un arquivo JSON (pasándolle o nome do arquivo) e devolva os datos do arquivo:
func abrirArquivo(nomeArquivo: String) -> Data? {
var datos: Data
// Busca o arquivo dentro do bundle da aplicación
guard let arquivo = Bundle.main.url(forResource: nomeArquivo,
withExtension: nil)
else {
fatalError("\(nomeArquivo) non atopado no proxecto.")
}
do {
// Intenta cargar os datos do arquivo
datos = try Data(contentsOf: arquivo)
} catch {
fatalError("Non foi posible cargar o arquivo \(nomeArquivo): \(error)")
}
return datos
}

Paso 3 de 5: Decodificar o JSON #
A partir do arquivo JSON leído no anterior apartado, devolve un array de obxectos que teña as propiedades que se correspondan coas claves do arquivo JSON.
func procesarJSON<TipoEstrutura: Decodable>(arquivo: Data) -> TipoEstrutura {
do {
// Intenta decodificar os datos JSON na estrutura fornecida
return try JSONDecoder().decode(TipoEstrutura.self, from: arquivo)
} catch {
// Se ocorre un erro na decodificación, a aplicación deterase
fatalError("Non foi posible parsear o arquivo: \(error)")
}
}

Paso 4 de 5: Almacenar os datos obtidos do JSON #
Por último,empregando as funcións dos pasos 2 e 3, vamos a almacenar os datos cargados no modelo creado no primeiro paso.
Para decodificar e almacenar os datos, utilizaremos unha clase 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
):
final class AlmacenCoches : ObservableObject {
@Published var coches: [CocheDataModel]
init () {
coches = procesarJSON(arquivo: abrirArquivo(nomeArquivo: "datosCoches.json")!)
}
}

Paso 5 de 5: Interface en SwiftUI #
Exemplo de interface empregando o modelo dos pasos anteriores:
struct VistaPrincipal: View {
@StateObject var almacenCoches: AlmacenCoches = AlmacenCoches()
var body: some View {
List {
ForEach(almacenCoches.coches){ coche in
CeldaLista(coche: coche)
}
}
}
}
struct CeldaLista: View {
var coche: CocheDataModel
var body: some View {
HStack {
Image(coche.imaxe)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 60)
Text(coche.nome)
}
}
}

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 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:
{
"home_page_url": "https://www.iessanmamede.com/category/novas",
"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",
"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
Paso 1 de 5: Crear a estrutura de datos #
Neste punto teremos que programar o modelo de datos que me permitirá almacenar e xestionar os datos procedentes do JSON.
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
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: Codable, Identifiable {
var id: String
var url: String
var titulo: String
var descricion: String
var imaxe: String
// Mapeo de chaves JSON -> Propiedades da clase
enum CodingKeys: String, CodingKey {
case id
case url
case titulo = "title" // O JSON usa "title", pero na clase de Swift usamos "titulo"
case descricion = "summary" // O JSON usa "summary", pero na clase de Swift usamos "descricion"
case imaxe = "image" // O JSON usa "image", pero na clase de Swift usamos "imaxe"
}
}
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.2: Creación do modelo de datos para as novas (NovasDataModel) #
Como comentamos máis arriba, as novas do IES San Mamede se corresponden coas chaves de primeiro nivel do JSON. Non vamos a procesar todas as chaves, concretamente:
- home_page_url
- icon
- items. Fixádevos que items é un array de novas que procesamos mediante
NovaDataModel
– paso 1.1
Así:
struct NovasDataModel: Codable {
var url: String
var icono: String
var novas: [NovaDataModel]
enum CodingKeys: String, CodingKey {
case url = "home_page_url" // O JSON usa "home_page_url", pero na clase de Swift usamos "url"
case icono = "icon" // O JSON usa "icon", pero na clase de Swift usamos "icono"
case novas = "items" // O JSON usa "items", pero na clase de Swift usamos "novas"
}
}

Paso 2 de 5: Obter o arquivo JSON #
Empregaremos unha función de que descargue un arquivo JSON (pasándolle a url) e devolva os datos do arquivo:
func abrirArquivo(url: URL) -> Data? {
do {
// Intenta descargar e cargar os datos do arquivo na URL fornecida
let data = try Data(contentsOf: url)
return data
} catch {
// Se ocorre un erro, imprímeo e devolve `nil`
print("Erro ao descargar o arquivo: \(error)")
return nil
}
}
Paso 3 de 5: Decodificar o JSON #
A partir do arquivo JSON descargado no anterior apartado, devolve un array de obxectos que teña as propiedades que se correspondan coas claves do arquivo JSON.
func procesarJSON<TipoEstrutura: Decodable>(arquivo: Data) -> TipoEstrutura {
do {
// Intenta decodificar os datos JSON na estrutura fornecida
return try JSONDecoder().decode(TipoEstrutura.self, from: arquivo)
} catch {
// Se ocorre un erro na decodificación, a aplicación deterase
fatalError("Non foi posible parsear o arquivo: \(error)")
}
}
Paso 4 de 5: Almacenar os datos obtidos do JSON #
Por último,empregando as funcións dos pasos 2 e 3, vamos a almacenar os datos cargados no modelo creado no primeiro paso.
Para decodificar e almacenar os datos, utilizaremos unha clase 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 AlmacenNovas
. Como comentei:
Teremos unha propiedade
Published
onde almacenaremos os datos empregando o modelo de datos programado no primeiro punto.Queremos almacenar as novas, polo tanto, no caso do exemplo, a propiedade será:
@Published var item: NovasDataModel
. Lembra, que enNovasDataModel
hay un propiedade chamadasNovasDataModel.novas
que é donde se atopará o array de novas.Así:
final class AlmacenNovas: ObservableObject {
@Published var item: NovasDataModel
let url = URL(string: "https://www.iessanmamede.com/category/novas/feed/json")
init() {
item = procesarJSON(arquivo: abrirArquivo(url: url!)!)
}
}

Paso 5 de 5: Interface en SwiftUI #
Exemplo de interface empregando o modelo dos pasos anteriores:

struct NovasView: View {
@StateObject var almacenNovas: AlmacenNovas = AlmacenNovas()
var body: some View {
NavigationView {
List(almacenNovas.item.novas) {
nova in
Link(destination: URL(string: nova.url)!) {
HStack {
ImaxeNovaView(url: nova.imaxe)
VStack(alignment: .leading) {
TituloNovaView(titulo: nova.titulo)
.foregroundStyle(.black)
ResumoNovaView(resumo: nova.descricion)
.foregroundStyle(.gray)
}
}
}
}
.navigationTitle("Novas IES San Mamede")
}
}
}
struct ImaxeNovaView: View {
var url: String
var body: some View {
AsyncImage(url: URL(string: url)) { image in
image
.resizable()
.scaledToFill()
.frame(width: 80, height: 80)
.cornerRadius(10)
} placeholder: {
ProgressView()
}
}
}
struct TituloNovaView: View {
var titulo: String
var body: some View {
Text(titulo).font(.title3).lineLimit(2).padding(.bottom, 3)
}
}
struct ResumoNovaView: View {
var resumo: String
var body: some View {
Text(resumo).font(.subheadline).lineLimit(2)
}
}