استخدم URLSession للشبكة وCodable لعقد JSON. احتفظ بالمفتاح على الخادم ولا تضعه داخل تطبيق iOS أو macOS أو watchOS.
- عرّف نموذج الاستجابة: احتفظ بـ rates وsource وmarket_session وdata_updated_at في النموذج، واستخدم Decimal للحسابات المالية.
- أبقِ المفتاح على الخادم: يستدعي تطبيق الهاتف خادمك، ويضيف الخادم Authorization. أما Swift على الخادم فيقرأ المفتاح من البيئة.
- عالج الحالة والحداثة: افحص حالة HTTP قبل فك JSON. فرّق بين 401 و429 واعرض وقت الملاحظة ومصدرها.
عرّف نموذج الاستجابة
احتفظ بـ rates وsource وmarket_session وdata_updated_at في النموذج، واستخدم 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) }}أبقِ المفتاح على الخادم
يستدعي تطبيق الهاتف خادمك، ويضيف الخادم Authorization. أما Swift على الخادم فيقرأ المفتاح من البيئة.
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 قبل فك JSON. فرّق بين 401 و429 واعرض وقت الملاحظة ومصدرها.
استخدم استدعاءً يومياً للتقارير أو كل ساعة للوحة تشغيلية. عند 429 خفّض الجدول ولا تنشئ حلقة إعادة محاولة فورية.
- احفظ السعر مع المصدر والوقت.
- استخدمه للأتمتة والتقدير والتحليل.
- لا تتعامل معه كسعر قابل للتنفيذ.