Files
sats-price/Sources/SatsPrice/Network/CoinbasePriceFetcher.swift

108 lines
3.3 KiB
Swift

// This is free software: you can redistribute and/or modify it
// under the terms of the GNU General Public License 3.0
// as published by the Free Software Foundation https://fsf.org
//
// CoinbasePriceFetcher.swift
// SatsPrice
//
// Created by Terry Yiu on 2/19/24.
//
import Foundation
private struct CoinbasePriceResponse: Codable {
let data: CoinbasePrice
}
private struct CoinbasePrice: Codable {
let amount: String
let base: String
let currency: String
}
private struct CoinbaseExchangeRatesResponse: Codable {
let data: CoinbaseExchangeRatesResponseData
}
private struct CoinbaseExchangeRatesResponseData: Codable {
let currency: String
let rates: [String: String]
}
class CoinbasePriceFetcher : PriceFetcher {
func urlString(toCurrency currency: Locale.Currency) -> String {
"https://api.coinbase.com/v2/prices/BTC-\(currency.identifier)/spot"
}
private static let urlStringForAllCurrencies: String = "https://api.coinbase.com/v2/exchange-rates?currency=BTC"
func convertBTC(toCurrency currency: Locale.Currency) async throws -> Decimal? {
do {
guard let urlComponents = URLComponents(string: urlString(toCurrency: currency)), let url = urlComponents.url else {
return nil
}
let (data, _) = try await URLSession.shared.data(from: url, delegate: nil)
let coinbasePriceResponse = try JSONDecoder().decode(CoinbasePriceResponse.self, from: data)
let coinbasePrice = coinbasePriceResponse.data
guard coinbasePrice.base == "BTC" && coinbasePrice.currency == currency.identifier else {
return nil
}
#if !SKIP
return Decimal(string: coinbasePrice.amount)
#else
return Decimal(coinbasePrice.amount)
#endif
} catch {
return nil
}
}
func convertBTC(toCurrencies currencies: [Locale.Currency]) async throws -> [Locale.Currency : Decimal] {
do {
guard !currencies.isEmpty else {
return [:]
}
if currencies.count == 1, let currency = currencies.first {
guard let price = try await convertBTC(toCurrency: currency) else {
return [:]
}
return [currency: price]
}
guard let urlComponents = URLComponents(string: CoinbasePriceFetcher.urlStringForAllCurrencies), let url = urlComponents.url else {
return [:]
}
let (data, _) = try await URLSession.shared.data(from: url, delegate: nil)
let coinbaseExchangeRatesResponse = try JSONDecoder().decode(CoinbaseExchangeRatesResponse.self, from: data)
let rates = coinbaseExchangeRatesResponse.data.rates
guard coinbaseExchangeRatesResponse.data.currency == "BTC" else {
return [:]
}
var results: [Locale.Currency : Decimal] = [:]
for currency in currencies {
if let price = rates[currency.identifier] {
#if !SKIP
results[currency] = Decimal(string: price)
#else
results[currency] = Decimal(price)
#endif
}
}
return results
} catch {
return [:]
}
}
}