Redes: URLSession y Codable
Consume APIs REST de forma moderna: peticiones async con URLSession y decodificación automática de JSON con Codable. De la nube a tu app, sin dolor.
Casi ninguna app vive aislada: consultan el tiempo, cargan un feed, sincronizan con un servidor. Con async/await y Codable, hacer una petición de red y convertir la respuesta JSON en objetos Swift es sorprendentemente limpio.
- Hacer una petición GET con
URLSessionyawait. - Decodificar JSON automáticamente con
Codable. - Manejar errores de red.
- Un ejemplo completo de principio a fin.
Codable: JSON ⇄ Swift
Un tipo que conforma a Codable se convierte a/desde JSON solo. Si los nombres coinciden con las claves del JSON, no escribes nada más:
struct Publicacion: Codable, Identifiable {
let id: Int
let title: String
let body: String
}
Para un JSON {"id": 1, "title": "Hola", "body": "…"}, este struct encaja directo. Si las claves difieren (JSON en snake_case), usas CodingKeys o un decoder con .convertFromSnakeCase.
La petición con URLSession
URLSession moderno es async: pides los datos con await y los tienes:
func cargarPublicaciones() async throws -> [Publicacion] {
let url = URL(string: "https://jsonplaceholder.typicode.com/posts")!
let (datos, respuesta) = try await URLSession.shared.data(from: url)
guard let http = respuesta as? HTTPURLResponse, http.statusCode == 200 else {
throw ErrorRed.codigoInvalido
}
return try JSONDecoder().decode([Publicacion].self, from: datos)
}
enum ErrorRed: Error { case codigoInvalido }
Tres pasos: pides los datos (await), compruebas el código HTTP, decodificas el JSON. El try propaga cualquier fallo (Nivel 1).
Conectarlo a la interfaz
Junta todo con el patrón .task y un modelo @Observable (Niveles 3 y 5):
@Observable
class Feed {
var publicaciones: [Publicacion] = []
var cargando = false
var error: String?
func cargar() async {
cargando = true
defer { cargando = false }
do {
publicaciones = try await cargarPublicaciones()
} catch {
self.error = "No se pudo cargar: \(error.localizedDescription)"
}
}
}
struct FeedView: View {
@State private var feed = Feed()
var body: some View {
List(feed.publicaciones) { post in
VStack(alignment: .leading) {
Text(post.title).font(.headline)
Text(post.body).font(.caption).foregroundStyle(.secondary)
}
}
.overlay { if feed.cargando { ProgressView() } }
.task { await feed.cargar() }
}
}
Detente a ver cuántas piezas se juntan en este ejemplo: Codable (Nivel 1) decodifica el JSON, async/await (5.1) hace la petición sin bloquear, @Observable (3.1) mantiene el estado, .task (5.1) lo carga al aparecer y lo cancela al salir, do/catch (1.5) maneja el error, y List (2.5) lo muestra. Esto no es un truco aislado: es cómo se construye de verdad la pantalla de datos de una app real. Cuando estas piezas te salen juntas y con naturalidad, ya piensas como un desarrollador iOS.
La red es el reino de lo incierto: no hay conexión, el servidor devuelve un error, el JSON viene raro. Un principiante asume el camino feliz; un profesional modela los tres estados —cargando, éxito, error— desde el principio (¿recuerdas los enums con valores asociados del Nivel 1?). Tu interfaz debe tener siempre respuesta para “está cargando” y “algo falló”, no solo para “todo bien”.
- Define un
struct Codableque encaje con una API pública (prueba jsonplaceholder.typicode.com). - Escribe una función
async throwsque descargue y decodifique el JSON. - Crea un modelo
@Observablecon estados de carga y error. - Muéstralo en una
Listcargando con.task. - Añade un
ProgressViewmientras carga y un mensaje si falla.