ให้ URLSession จัดการเครือข่ายและ Codable จัดการ JSON คีย์จริงควรอยู่ที่ server ไม่ใช่ในแอป iOS, macOS หรือ watchOS.
- กำหนด response model: เก็บ rates, source, market_session และ data_updated_at ใน model และใช้ Decimal กับจำนวนเงิน.
- เก็บคีย์ไว้ที่ server: แอปมือถือเรียก backend ของคุณ แล้ว backend เติม Authorization ส่วน Swift ฝั่ง server อ่านคีย์จาก environment ได้.
- จัดการสถานะและ freshness: ตรวจสอบ HTTP status ก่อน decode แยก 401 กับ 429 และแสดงเวลาและ source ให้ผู้ใช้เห็น.
กำหนด response model
เก็บ rates, source, market_session และ data_updated_at ใน model และใช้ Decimal กับจำนวนเงิน.
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) }}เก็บคีย์ไว้ที่ server
แอปมือถือเรียก backend ของคุณ แล้ว backend เติม Authorization ส่วน Swift ฝั่ง server อ่านคีย์จาก environment ได้.
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)จัดการสถานะและ freshness
ตรวจสอบ HTTP status ก่อน decode แยก 401 กับ 429 และแสดงเวลาและ source ให้ผู้ใช้เห็น.
รายงานรายวันใช้วันละครั้ง ส่วน dashboard อาจใช้ทุกชั่วโมง เมื่อเจอ 429 ให้ลดความถี่ ไม่ใช่สร้าง retry ทันที.
- เก็บอัตราพร้อม source และเวลา.
- ใช้กับ automation และการวิเคราะห์.
- อย่าใช้เป็นราคาที่ execute ได้.