GuíasTutoriales

Obtener tipos de cambio en Swift con URLSession

Crea un cliente Swift con URLSession, Codable, URLComponents y un modelo explícito de frescura para mostrar tipos de cambio en apps iOS y macOS.

8 sept 2026 · 6 min de lectura

Usa URLSession para la red y Codable para el contrato JSON. Mantén la clave en tu servidor; nunca la incluyas en una app iOS, macOS o watchOS.

  • Define el modelo: El modelo conserva rates, source, market_session y data_updated_at. Decimal evita empezar los cálculos monetarios con errores de Double.
  • Mantén la clave en el servidor: Una app cliente debe llamar a tu backend, que añade Authorization. En Swift del lado servidor puedes leer ProcessInfo.environment.
  • Maneja estado y frescura: Comprueba el estado HTTP antes de decodificar. Trata 401 y 429 de forma distinta y muestra el timestamp y el contexto de fuente en la interfaz.

Define el modelo

El modelo conserva rates, source, market_session y data_updated_at. Decimal evita empezar los cálculos monetarios con errores de Double.

swift · ExchangeRateClient.swift
import Foundationfinal class NoRedirects: NSObject, URLSessionTaskDelegate {    func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) {        completionHandler(nil)    }}struct Latest: Decodable {    let base: String    let source: String    let marketSession: String    let dataUpdatedAt: String    let rates: [String: Decimal]}struct ExchangeRateClient {    let session: URLSession    let apiKey: String?    func latest(base: String) async throws -> Latest {        let normalizedBase = base.uppercased()        guard normalizedBase.count == 3, normalizedBase.allSatisfy({ $0.isASCII && $0.isLetter }) else {            throw URLError(.badURL)        }        guard var components = URLComponents(string: "https://api.exchangerate.dev/v1/latest/\(normalizedBase)") else {            throw URLError(.badURL)        }        components.queryItems = [URLQueryItem(name: "symbols", value: "EUR,GBP,JPY")]        guard let url = components.url else { throw URLError(.badURL) }        var request = URLRequest(url: url)        request.timeoutInterval = 10        if let apiKey { request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization") }        let (data, response) = try await session.data(for: request)        guard let http = response as? HTTPURLResponse, (200..<300).contains(http.statusCode) else {            throw URLError(.badServerResponse)        }        let decoder = JSONDecoder()        decoder.keyDecodingStrategy = .convertFromSnakeCase        return try decoder.decode(Latest.self, from: data)    }}

Mantén la clave en el servidor

Una app cliente debe llamar a tu backend, que añade Authorization. En Swift del lado servidor puedes leer ProcessInfo.environment.

swift · server setup
let key = ProcessInfo.processInfo.environment["EXCHANGERATE_API_KEY"]let session = URLSession(configuration: .ephemeral, delegate: NoRedirects(), delegateQueue: nil)let client = ExchangeRateClient(    session: session,    apiKey: key)let latest = try await client.latest(base: "USD")print(latest.rates["EUR"] as Any, latest.source, latest.dataUpdatedAt)

Maneja estado y frescura

Comprueba el estado HTTP antes de decodificar. Trata 401 y 429 de forma distinta y muestra el timestamp y el contexto de fuente en la interfaz.

Conserva el contexto del tipo

Programa una tarea diaria para informes o una tarea horaria para un panel. Si recibes 429, reduce la frecuencia en lugar de crear reintentos inmediatos.

  • Guarda el tipo junto a su fuente y hora.
  • Usa el valor para automatización y análisis.
  • No lo trates como una cotización ejecutable.

Preguntas

¿Puedo poner la clave en iOS?

No. Una clave dentro del bundle se puede extraer; usa un backend.

¿Funciona sin clave?

Sí, para evaluación dentro de los límites anónimos.

¿Por qué Decimal?

Evita artefactos de coma flotante al multiplicar y redondear importes.

¿Qué hago después de un 429?

Reduce la frecuencia, evita reintentos inmediatos y respeta el límite del plan.

Sigue leyendo

Empieza con una clave API gratuita

5.000 solicitudes al mes con el plan gratuito. Sin tarjeta de crédito.

Obtén una clave gratuita