Contenido del curso

Operaciones CRUD en un proyecto con MVVM

Capa de datos con Clean Architecture en Swift

Resumen

Conectar una app iOS a una API externa siguiendo Clean Architecture en Swift implica trabajar de adentro hacia afuera: primero los modelos, luego la red y finalmente el repositorio que comunica esta capa con el dominio. Aquí tienes la ruta práctica para construir la capa de datos usando Xcode, URLSession y JSONDecoder, con la API de The Movie DB como referencia.

¿Por qué empezar por la capa de datos en Clean Architecture?

Clean Architecture se representa con tres circunferencias concéntricas que definen cómo estructurar un proyecto. La capa más profunda contiene las entidades o modelos, y desde ahí se avanza hacia afuera [00:35].

El orden lógico es construir primero los modelos, después la capa de datos (red y repositorios), luego la capa de dominio con los casos de uso, y al final la capa de presentación con vistas y estados. Trabajar así garantiza que las dependencias siempre apunten hacia el centro.

¿Qué hace la capa de datos en Clean Architecture? Es la responsable de obtener información desde fuentes externas como APIs, decodificarla en modelos y exponerla mediante un repositorio que el dominio pueda consumir sin conocer los detalles de implementación.

¿Cómo defino los endpoints de una API en Swift?

El primer paso es crear un archivo Endpoint dentro de la carpeta Network. Aquí se concentran las rutas a las que vas a apuntar, organizadas con enums para que el código quede limpio y predecible [02:05].

¿Qué enums necesito para estructurar las peticiones?

Después de importar Foundation, declara dos enums. El primero, HTTPMethod de tipo String, define los verbos disponibles:

  • case get = "GET" para obtener información.
  • case post = "POST" para enviar información a un servicio externo.

El segundo enum, llamado Endpoint, define los casos específicos que necesitas: popularShows para el listado general y tvShowDetails(id: Int) para el detalle de un programa, que requiere sí o sí un identificador [03:40].

¿Cómo construyo la URL base con API key?

Dentro del enum Endpoint agrega una variable computada url de tipo URL. Define dos constantes privadas: la URL base https://api.themoviedb.org/3 y el API key que obtienes desde la sección API de tu cuenta en TheMovieDB [05:30].

Luego usa un switch para resolver cada caso:

  • En popularShows arma la cadena con baseURL + "/tv/popular?api_key=\(apiKey)&language=es&page=1".
  • En tvShowDetails(id) concatena el identificador: baseURL + "/tv/\(id)?api_key=\(apiKey)&language=es".

Finaliza retornando la URL forzada para garantizar el tipo de retorno. Añade también una variable method: HTTPMethod que devuelva .get para ambos casos.

¿Cómo creo un cliente HTTP reutilizable en Swift?

Dentro del directorio Network crea otro archivo llamado ApiClient. Esta clase final concentra la lógica para ejecutar las peticiones a la API externa y decodificar la respuesta [09:15].

Declara una función genérica:

swift func request<T: Codable>(_ endpoint: Endpoint) async throws -> T { var urlRequest = URLRequest(url: endpoint.url) urlRequest.httpMethod = endpoint.method.rawValue

let (data, response) = try await URLSession.shared.data(for: urlRequest) guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else { throw URLError(.badServerResponse) } let decoder = JSONDecoder() return try decoder.decode(T.self, from: data)

}

El uso de async/await permite que la solicitud sea asíncrona y manejable con try. La validación con el rango 200...299 confirma que la respuesta HTTP fue exitosa antes de decodificar.

¿Para qué sirve JSONDecoder en Swift? Convierte la respuesta JSON recibida en objetos Codable que tu app puede usar directamente, como un TVShow o un TVShowDetails.

¿Qué papel juega el repositorio en la capa de datos?

El directorio Repositories actúa como el único puente entre la capa de datos y la capa de dominio. Todo lo que ocurre dentro de la capa de datos queda encapsulado y solo se expone mediante un protocolo [15:20].

¿Cómo declaro el protocolo TVShowRepository?

Crea un archivo TVShowRepository con un protocol que declare las dos funciones públicas:

  • func fetchPopularTVShows() async throws -> [TVShow] para el listado.
  • func fetchTVShowDetails(id: Int) async throws -> TVShowDetails para el detalle.

Este protocolo refuerza la independencia de la fuente de datos: la capa de dominio solo conoce el contrato, nunca la implementación concreta.

¿Cómo implemento el repositorio inyectando el ApiClient?

En TVShowRepositoryImpl adopta el protocolo y declara una propiedad privada private let apiClient: ApiClient. Inicialízala mediante inyección de dependencias en el init, lo que facilita pruebas y desacoplamiento [18:40].

Para fetchPopularTVShows, llama a apiClient.request enviando .popularShows y casteando el tipo a PopularShowResponse. Retorna response.results, que contiene el arreglo de TVShow.

Para fetchTVShowDetails, ejecuta apiClient.request(.tvShowDetails(id: id)) y devuelve directamente el objeto TVShowDetails.

Con esto cierras la capa de datos: tienes los modelos, la configuración de red y un repositorio que oculta los detalles internos detrás de un contrato claro. Cuéntame en los comentarios qué API estás integrando con esta arquitectura y qué retos te ha generado.