指南教程

用 Swift 的 URLSession 获取汇率

使用 URLSession、Codable 和 URLComponents,并保留汇率响应的更新时间信息。

2026年9月8日 · 阅读约 6 分钟

网络请求使用 URLSession,JSON 使用 Codable。真实密钥应放在服务器,不能嵌入 iOS、macOS 或 watchOS 应用。

  • 定义响应模型: 模型保留 rates、source、market_session 和 data_updated_at。金额计算使用 Decimal,避免 Double 的二进制误差。
  • 把密钥留在服务器: 移动端调用自己的后端,由后端添加 Authorization。服务端 Swift 可以从环境变量读取密钥。
  • 处理状态和新鲜度: 解码前检查 HTTP 状态码,分别处理 401 和 429,并向用户展示时间和来源上下文。

定义响应模型

模型保留 rates、source、market_session 和 data_updated_at。金额计算使用 Decimal,避免 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)    }}

把密钥留在服务器

移动端调用自己的后端,由后端添加 Authorization。服务端 Swift 可以从环境变量读取密钥。

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)

处理状态和新鲜度

解码前检查 HTTP 状态码,分别处理 401 和 429,并向用户展示时间和来源上下文。

同时保留汇率上下文

日报可以每天调用一次,运营看板可以每小时调用一次。收到 429 时降低频率,不要立即制造重试风暴。

  • 把汇率与来源和时间一起保存。
  • 用于自动化、估算、报表和分析。
  • 不要当作可成交报价。

常见问题

可以把密钥放进 iOS 吗?

不可以,应用包中的密钥可以被提取,应放在服务器。

不使用密钥能运行吗?

可以,在匿名评估限制内运行。

为什么用 Decimal?

金额乘法和舍入时可避免 Double 的二进制浮点误差。

收到 429 后怎么办?

降低请求频率,不要立即重试,并遵守套餐限制。

继续阅读

从免费API密钥开始

免费套餐每月提供5,000次请求。无需信用卡。

获取免费密钥