ガイドチュートリアル

SwiftのURLSessionで為替レートを取得する

URLSession、Codable、URLComponentsを使い、更新情報を保持するSwiftクライアントを作る。

2026年9月8日 · 6分で読める

ネットワークはURLSession、JSONはCodableで扱う。キーはサーバーに置き、iOSやmacOSのアプリバンドルには埋め込まない。

  • レスポンスモデルを定義する: ratesとsource、market_session、data_updated_atをモデルに保持する。金額計算ではDoubleではなくDecimalを使う。
  • キーをサーバー側に置く: モバイルアプリは自分のバックエンドを呼び、バックエンドがAuthorizationを付ける。サーバーSwiftなら環境変数から読める。
  • 状態と鮮度を処理する: JSONデコード前にHTTPステータスを確認する。401と429を分けて扱い、時刻とsourceを画面に残す。

レスポンスモデルを定義する

ratesとsource、market_session、data_updated_atをモデルに保持する。金額計算ではDoubleではなくDecimalを使う。

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)

状態と鮮度を処理する

JSONデコード前にHTTPステータスを確認する。401と429を分けて扱い、時刻とsourceを画面に残す。

レートの意味を一緒に保存する

日次レポートなら1日1回、運用画面なら1時間ごとを目安にする。429では即時リトライを増やさず、頻度を下げる。

  • レートとsource、更新時刻を保存する。
  • 自動化や分析の参考値として使う。
  • 約定可能な価格として扱わない。

よくある質問

iOSにキーを入れてよいですか?

いいえ。アプリから抽出できるため、サーバーに置きます。

キーなしで動きますか?

匿名評価の範囲で動きます。

なぜDecimalですか?

金額計算と丸めでDoubleの誤差を避けるためです。

429の後はどうしますか?

頻度を下げ、すぐに再試行せず、プランの制限を守ります。

関連記事

無料のAPIキーで始める

無料プランで月5,000リクエスト。クレジットカードは不要です。

無料キーを取得