الأدلةدروس

جلب أسعار الصرف في Swift باستخدام URLSession

أنشئ عميلاً بـ URLSession وCodable وURLComponents مع نموذج واضح لحداثة السعر، لعرض أسعار الصرف في تطبيقات iOS وmacOS بلغة Swift.

8 سبتمبر 2026 · قراءة خلال 6 دقائق

استخدم 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 للحسابات المالية.

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 قبل فك JSON. فرّق بين 401 و429 واعرض وقت الملاحظة ومصدرها.

احفظ سياق السعر

استخدم استدعاءً يومياً للتقارير أو كل ساعة للوحة تشغيلية. عند 429 خفّض الجدول ولا تنشئ حلقة إعادة محاولة فورية.

  • احفظ السعر مع المصدر والوقت.
  • استخدمه للأتمتة والتقدير والتحليل.
  • لا تتعامل معه كسعر قابل للتنفيذ.

أسئلة

هل أضع المفتاح في iOS؟

لا، يمكن استخراج المفتاح من حزمة التطبيق؛ ضعه على الخادم.

هل يعمل بلا مفتاح؟

نعم ضمن حدود التقييم المجهول.

لماذا Decimal؟

لتجنب أخطاء الفاصلة العائمة عند ضرب المبلغ وتقريبه.

ماذا أفعل بعد 429؟

خفّض الجدول وتجنب الإعادة الفورية والتزم بحدود الباقة.

تابع القراءة

ابدأ بمفتاح API مجاني

5,000 طلب شهريًا ضمن الخطة المجانية. لا حاجة إلى بطاقة ائتمان.

احصل على مفتاح مجاني