URLSession + Decodable en Swift

URLSession + Decodable en Swift

Hoy vamos a ver cómo hacer una petición con URLSession y usar Decodable en Swift. Hemos visto estas dos clase por separado, pero hoy vamos a ver un ejemplo más práctico en Swift.

SwiftBeta

Ya hemos visto URLSession y Decodable en posts anteriores. Vamos a ver cómo funcionan estas dos APIs tan usadas en el mundo iOS de forma conjunta.

La mayoría de apps necesitan información dinámica para mostrar a los usuarios. Ejemplos claros son: apps con información meteorológica, Instagram, TikTok, etc. Para recibir esta información lo más común es usar peticiones HTTP.

Cómo la teoría ya la hemos visto, vamos directamente a la práctica:

  • Usaremos la API de iTunes de Apple para buscar música
  • Mapearemos el JSON a un modelo de nuestro dominio

Puedes usar Paw para ver el resultado de nuestra petición HTTP al endpoint:

https://itunes.apple.com/search?term=jack+johnson&limit=25

Aquí podrás encontrar el post donde hablamos de esta aplicación muy útil para desarrolladores.

Paw
Paw es una herramienta muy útil y flexible para probar nuestras peticiones HTTP. Te muestro como usar la app desde el minuto 1 y como exportar endpoints para que puedas usarlos en Swift (o cualquier otro lenguaje).

Crear Petición HTTP

Primero de todo crearemos nuestros modelos de domino:

struct SearchResponseDataModel: Decodable {
    let resultCount: Int
    let results: [TrackDataModel]
}

struct TrackDataModel: Decodable {
    let artistId: Int
    let kind: String?
    let name: String?
    let artistName: String
    let artworkURL: String
    
    enum CodingKeys: String, CodingKey {
        case artistId
        case kind
        case name = "trackName"
        case artistName = "artistName"
        case artworkURL = "artworkUrl60"
    }
}
    

Y a continuación creamos nuestra clase APIClient con un único método:

final class APIClient {
    func searchTracksOnItunes(completionBlock: @escaping ([TrackDataModel]) -> ()) {
        // 1
        let url = URL(string: "https://itunes.apple.com/search?term=jack+johnson")!
        // 2
        let task = URLSession.shared.dataTask(with: url) {
            data, response, error in
            // 3
            let dataModel = try! JSONDecoder().decode(SearchResponseDataModel.self, from: data!)
            completionBlock(dataModel.results)
        }
        // 4
        task.resume()
    }
}

La clase APIClient tiene un único método llamado searchTracksOnItunes(completionBlock:). Dentro de él hay distintos pasos:

  1. Crear la URL con los parámetros necesarios para buscar en la base de datos de iTunes, en este caso es "Jack Johnson".
  2. Pasamos la URL como parámetro al singleton URLSession y declaramos su trailing closure para cuando la llamada obtenga un resultado.
  3. Si el resultado es correcto, parseamos el JSON recibido en un modelo nuestro de dominio, en este caso se llama SearchResponseDataModel.
  4. Ejecutamos la petición HTTP. Este paso es muy importante, ya que es el trigger para relizar la petición.

Para ejecutar el siguiente código solo nos faltaría crear una instancia de nuestro APIClient y llamar al método searchTracksOnItunes(completionBlock:)

let apiClient = APIClient()
apiClient.searchTracksOnItunes {
    print("DataModel \($0)")
} 

Como ves, aunque funciona, este código no es escalable. Tiene 3 responsabilidades:

  1. Crear la URL del endpoint
  2. Realizar la petición HTTP
  3. Transformar el JSON a nuestro modelo de negocio

Si estás trabajando por tu cuenta puede que te sirva, pero a medida que pase el tiempo o si trabajas en una empresa con más compañeros, verás que seguir este camino no es el ideal. Es poco escalable y poco mantenible.

Por ejemplo: ¿qué harías si quieres cambiar de entorno, me refiero un docker, beta, producción? O en caso de tener un token para que autentifique al usuario, ¿dónde y cómo lo metes en el header de todas las peticiones?

No te preocupes, te hago un pequeño spoiler. En los siguientes posts crearemos un APIClient con 4 componentes. Cada componente tendrá su propia responsabilidad y serán: un Endpoint, Router, Parser y un Requester.
Lo veremos en detalle en los siguientes posts relacionados con Network.


Hasta aquí el post de hoy, gracias por leernos! 🤓
Si tienes preguntas no dudes en contactar con nosotros a través de Twitter

Network