Add support for currency selection
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# SatsPrice
|
# SatsPrice
|
||||||
|
|
||||||
This app fetches the price of Bitcoin relative to the United States Dollar from multiple sources, and converts inputted amounts between Sats, BTC, and USD.
|
This app fetches the price of Bitcoin relative to common ISO currencies from multiple sources, and converts inputted amounts between Sats, BTC, and the selected ISO currency.
|
||||||
|
|
||||||
This is a free [Skip](https://skip.tools) dual-platform app project.
|
This is a free [Skip](https://skip.tools) dual-platform app project.
|
||||||
It builds a native app for both iOS and Android.
|
It builds a native app for both iOS and Android.
|
||||||
|
|||||||
@@ -26,14 +26,14 @@ public struct ContentView: View {
|
|||||||
@MainActor
|
@MainActor
|
||||||
func updatePrice() async {
|
func updatePrice() async {
|
||||||
do {
|
do {
|
||||||
guard let price = try await priceFetcherDelegator.btcToUsd() else {
|
guard let price = try await priceFetcherDelegator.convertBTC(toCurrency: satsViewModel.selectedCurrency) else {
|
||||||
satsViewModel.btcToUsdString = ""
|
satsViewModel.btcToCurrencyString = ""
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
satsViewModel.btcToUsdString = "\(price)"
|
satsViewModel.btcToCurrencyString = "\(price)"
|
||||||
} catch {
|
} catch {
|
||||||
satsViewModel.btcToUsdString = ""
|
satsViewModel.btcToCurrencyString = ""
|
||||||
}
|
}
|
||||||
satsViewModel.lastUpdated = Date.now
|
satsViewModel.lastUpdated = Date.now
|
||||||
}
|
}
|
||||||
@@ -47,8 +47,18 @@ public struct ContentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Picker("Currency", selection: $satsViewModel.selectedCurrency) {
|
||||||
|
ForEach(satsViewModel.currencies, id: \.self) {
|
||||||
|
if let localizedCurrency = Locale.current.localizedString(forCurrencyCode: $0.identifier) {
|
||||||
|
Text("\($0.identifier) - \(localizedCurrency)")
|
||||||
|
} else {
|
||||||
|
Text($0.identifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
TextField("", text: $satsViewModel.btcToUsdString)
|
TextField("", text: $satsViewModel.btcToCurrencyString)
|
||||||
.disabled(priceSource != .manual)
|
.disabled(priceSource != .manual)
|
||||||
#if os(iOS) || SKIP
|
#if os(iOS) || SKIP
|
||||||
.keyboardType(.decimalPad)
|
.keyboardType(.decimalPad)
|
||||||
@@ -64,7 +74,7 @@ public struct ContentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} header: {
|
} header: {
|
||||||
Text("1 BTC to USD")
|
Text("1 BTC to \(satsViewModel.selectedCurrency.identifier)")
|
||||||
} footer: {
|
} footer: {
|
||||||
if priceSource != .manual, let lastUpdated = satsViewModel.lastUpdated {
|
if priceSource != .manual, let lastUpdated = satsViewModel.lastUpdated {
|
||||||
Text("Last updated: \(dateFormatter.string(from: lastUpdated))")
|
Text("Last updated: \(dateFormatter.string(from: lastUpdated))")
|
||||||
@@ -90,17 +100,23 @@ public struct ContentView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
TextField("", text: $satsViewModel.usdString)
|
TextField("", text: $satsViewModel.currencyValueString)
|
||||||
#if os(iOS) || SKIP
|
#if os(iOS) || SKIP
|
||||||
.keyboardType(.decimalPad)
|
.keyboardType(.decimalPad)
|
||||||
#endif
|
#endif
|
||||||
} header: {
|
} header: {
|
||||||
Text("USD")
|
Text(satsViewModel.selectedCurrency.identifier)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.task {
|
.task {
|
||||||
await updatePrice()
|
await updatePrice()
|
||||||
}
|
}
|
||||||
|
.onChange(of: satsViewModel.selectedCurrency) { newCurrency in
|
||||||
|
satsViewModel.lastUpdated = nil
|
||||||
|
Task {
|
||||||
|
await updatePrice()
|
||||||
|
}
|
||||||
|
}
|
||||||
.onChange(of: priceSource) { newPriceSource in
|
.onChange(of: priceSource) { newPriceSource in
|
||||||
satsViewModel.lastUpdated = nil
|
satsViewModel.lastUpdated = nil
|
||||||
priceFetcherDelegator.priceSource = newPriceSource
|
priceFetcherDelegator.priceSource = newPriceSource
|
||||||
|
|||||||
@@ -11,35 +11,35 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
private struct CoinGeckoPriceResponse: Codable {
|
private struct CoinGeckoPriceResponse: Codable {
|
||||||
let bitcoin: CoinGeckoPrice
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct CoinGeckoPrice: Codable {
|
|
||||||
#if !SKIP
|
#if !SKIP
|
||||||
let usd: Decimal
|
let bitcoin: [String: Decimal]
|
||||||
#else
|
#else
|
||||||
let usd: String
|
let bitcoin: [String: String]
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
class CoinGeckoPriceFetcher : PriceFetcher {
|
class CoinGeckoPriceFetcher : PriceFetcher {
|
||||||
private static let urlString = "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd&precision=18"
|
func urlString(toCurrency currency: Locale.Currency) -> String {
|
||||||
|
"https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=\(currency.identifier.lowercased())&precision=18"
|
||||||
|
}
|
||||||
|
|
||||||
func btcToUsd() async throws -> Decimal? {
|
func convertBTC(toCurrency currency: Locale.Currency) async throws -> Decimal? {
|
||||||
do {
|
do {
|
||||||
guard let urlComponents = URLComponents(string: CoinGeckoPriceFetcher.urlString), let url = urlComponents.url else {
|
guard let urlComponents = URLComponents(string: urlString(toCurrency: currency)), let url = urlComponents.url else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let (data, _) = try await URLSession.shared.data(from: url, delegate: nil)
|
let (data, _) = try await URLSession.shared.data(from: url, delegate: nil)
|
||||||
|
|
||||||
let priceResponse = try JSONDecoder().decode(CoinGeckoPriceResponse.self, from: data)
|
let priceResponse = try JSONDecoder().decode(CoinGeckoPriceResponse.self, from: data)
|
||||||
let price = priceResponse.bitcoin
|
guard let price = priceResponse.bitcoin[currency.identifier.lowercased()] else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
#if !SKIP
|
#if !SKIP
|
||||||
return price.usd
|
return price
|
||||||
#else
|
#else
|
||||||
return Decimal(price.usd)
|
return Decimal(price)
|
||||||
#endif
|
#endif
|
||||||
} catch {
|
} catch {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -21,13 +21,13 @@ private struct CoinbasePrice: Codable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class CoinbasePriceFetcher : PriceFetcher {
|
class CoinbasePriceFetcher : PriceFetcher {
|
||||||
private static let urlString = "https://api.coinbase.com/v2/prices/BTC-USD/spot"
|
func urlString(toCurrency currency: Locale.Currency) -> String {
|
||||||
private static let btc = "BTC"
|
"https://api.coinbase.com/v2/prices/BTC-\(currency.identifier)/spot"
|
||||||
private static let usd = "USD"
|
}
|
||||||
|
|
||||||
func btcToUsd() async throws -> Decimal? {
|
func convertBTC(toCurrency currency: Locale.Currency) async throws -> Decimal? {
|
||||||
do {
|
do {
|
||||||
guard let urlComponents = URLComponents(string: CoinbasePriceFetcher.urlString), let url = urlComponents.url else {
|
guard let urlComponents = URLComponents(string: urlString(toCurrency: currency)), let url = urlComponents.url else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ class CoinbasePriceFetcher : PriceFetcher {
|
|||||||
let coinbasePriceResponse = try JSONDecoder().decode(CoinbasePriceResponse.self, from: data)
|
let coinbasePriceResponse = try JSONDecoder().decode(CoinbasePriceResponse.self, from: data)
|
||||||
let coinbasePrice = coinbasePriceResponse.data
|
let coinbasePrice = coinbasePriceResponse.data
|
||||||
|
|
||||||
guard coinbasePrice.base == CoinbasePriceFetcher.btc && coinbasePrice.currency == CoinbasePriceFetcher.usd else {
|
guard coinbasePrice.base == "BTC" && coinbasePrice.currency == currency.identifier else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import Foundation
|
|||||||
|
|
||||||
/// Fake price fetcher that returns a randomized price. Useful for development testing without requiring a network call.
|
/// Fake price fetcher that returns a randomized price. Useful for development testing without requiring a network call.
|
||||||
class FakePriceFetcher: PriceFetcher {
|
class FakePriceFetcher: PriceFetcher {
|
||||||
func btcToUsd() async throws -> Decimal? {
|
func convertBTC(toCurrency currency: Locale.Currency) async throws -> Decimal? {
|
||||||
Decimal(Double.random(in: 10000...100000))
|
Decimal(Double.random(in: 10000...100000))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import Foundation
|
|||||||
class ManualPriceFetcher: PriceFetcher {
|
class ManualPriceFetcher: PriceFetcher {
|
||||||
var price: Decimal = Decimal(1)
|
var price: Decimal = Decimal(1)
|
||||||
|
|
||||||
func btcToUsd() async throws -> Decimal? {
|
func convertBTC(toCurrency currency: Locale.Currency) async throws -> Decimal? {
|
||||||
return price
|
return price
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,5 +11,5 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
protocol PriceFetcher {
|
protocol PriceFetcher {
|
||||||
func btcToUsd() async throws -> Decimal?
|
func convertBTC(toCurrency currency: Locale.Currency) async throws -> Decimal?
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class PriceFetcherDelegator: PriceFetcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func btcToUsd() async throws -> Decimal? {
|
func convertBTC(toCurrency currency: Locale.Currency) async throws -> Decimal? {
|
||||||
return try await delegate.btcToUsd()
|
return try await delegate.convertBTC(toCurrency: currency)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,24 @@
|
|||||||
"" : {
|
"" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"1 BTC to USD" : {
|
"%@ - %@" : {
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "%1$@ - %2$@"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"1 BTC to %@" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"BTC" : {
|
"BTC" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Currency" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Last updated: %@" : {
|
"Last updated: %@" : {
|
||||||
|
|
||||||
@@ -18,9 +31,6 @@
|
|||||||
},
|
},
|
||||||
"Sats" : {
|
"Sats" : {
|
||||||
|
|
||||||
},
|
|
||||||
"USD" : {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"version" : "1.0"
|
"version" : "1.0"
|
||||||
|
|||||||
@@ -15,26 +15,40 @@ import SwiftUI
|
|||||||
class SatsViewModel: ObservableObject {
|
class SatsViewModel: ObservableObject {
|
||||||
@Published var lastUpdated: Date?
|
@Published var lastUpdated: Date?
|
||||||
|
|
||||||
@Published var btcToUsdStringInternal: String = ""
|
@Published var btcToCurrencyStringInternal: String = ""
|
||||||
@Published var satsStringInternal: String = ""
|
@Published var satsStringInternal: String = ""
|
||||||
@Published var btcStringInternal: String = ""
|
@Published var btcStringInternal: String = ""
|
||||||
@Published var usdStringInternal: String = ""
|
@Published var currencyValueStringInternal: String = ""
|
||||||
|
@Published var selectedCurrency: Locale.Currency = Locale.current.currency ?? Locale.Currency("USD")
|
||||||
|
|
||||||
var btcToUsdString: String {
|
var currencies: [Locale.Currency] {
|
||||||
|
let commonISOCurrencyCodes = Set(Locale.commonISOCurrencyCodes)
|
||||||
|
let currentCurrency = Locale.current.currency ?? Locale.Currency("USD")
|
||||||
|
if commonISOCurrencyCodes.contains(currentCurrency.identifier) {
|
||||||
|
return Locale.commonISOCurrencyCodes.map { Locale.Currency($0) }
|
||||||
|
} else {
|
||||||
|
var commonAndCurrentCurrencies = Locale.commonISOCurrencyCodes
|
||||||
|
commonAndCurrentCurrencies.append(currentCurrency.identifier)
|
||||||
|
commonAndCurrentCurrencies.sort()
|
||||||
|
return commonAndCurrentCurrencies.map { Locale.Currency($0) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var btcToCurrencyString: String {
|
||||||
get {
|
get {
|
||||||
btcToUsdStringInternal
|
btcToCurrencyStringInternal
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
guard btcToUsdStringInternal != newValue else {
|
guard btcToCurrencyStringInternal != newValue else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
btcToUsdStringInternal = newValue
|
btcToCurrencyStringInternal = newValue
|
||||||
|
|
||||||
if let btc, let btcToUsd {
|
if let btc, let btcToCurrency {
|
||||||
usdStringInternal = (btc * btcToUsd).formatString()
|
currencyValueStringInternal = (btc * btcToCurrency).formatString()
|
||||||
} else {
|
} else {
|
||||||
usdStringInternal = ""
|
currencyValueStringInternal = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,14 +71,14 @@ class SatsViewModel: ObservableObject {
|
|||||||
let btc = sats.divide(Decimal(100000000), 20, java.math.RoundingMode.DOWN)
|
let btc = sats.divide(Decimal(100000000), 20, java.math.RoundingMode.DOWN)
|
||||||
#endif
|
#endif
|
||||||
btcStringInternal = btc.formatString()
|
btcStringInternal = btc.formatString()
|
||||||
if let btcToUsd {
|
if let btcToCurrency {
|
||||||
usdStringInternal = (btc * btcToUsd).formatString()
|
currencyValueStringInternal = (btc * btcToCurrency).formatString()
|
||||||
} else {
|
} else {
|
||||||
usdStringInternal = ""
|
currencyValueStringInternal = ""
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
btcStringInternal = ""
|
btcStringInternal = ""
|
||||||
usdStringInternal = ""
|
currencyValueStringInternal = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,35 +98,35 @@ class SatsViewModel: ObservableObject {
|
|||||||
let sats = btc * Decimal(100000000)
|
let sats = btc * Decimal(100000000)
|
||||||
satsStringInternal = sats.formatString()
|
satsStringInternal = sats.formatString()
|
||||||
|
|
||||||
if let btcToUsd {
|
if let btcToCurrency {
|
||||||
usdStringInternal = (btc * btcToUsd).formatString()
|
currencyValueStringInternal = (btc * btcToCurrency).formatString()
|
||||||
} else {
|
} else {
|
||||||
usdStringInternal = ""
|
currencyValueStringInternal = ""
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
satsStringInternal = ""
|
satsStringInternal = ""
|
||||||
usdStringInternal = ""
|
currencyValueStringInternal = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var usdString: String {
|
var currencyValueString: String {
|
||||||
get {
|
get {
|
||||||
usdStringInternal
|
currencyValueStringInternal
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
guard usdStringInternal != newValue else {
|
guard currencyValueStringInternal != newValue else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
usdStringInternal = newValue
|
currencyValueStringInternal = newValue
|
||||||
|
|
||||||
if let usd {
|
if let currencyValue {
|
||||||
if let btcToUsd {
|
if let btcToCurrency {
|
||||||
#if !SKIP
|
#if !SKIP
|
||||||
let btc = usd / btcToUsd
|
let btc = currencyValue / btcToCurrency
|
||||||
#else
|
#else
|
||||||
let btc = usd.divide(btcToUsd, 20, java.math.RoundingMode.DOWN)
|
let btc = currencyValue.divide(btcToCurrency, 20, java.math.RoundingMode.DOWN)
|
||||||
#endif
|
#endif
|
||||||
btcStringInternal = btc.formatString()
|
btcStringInternal = btc.formatString()
|
||||||
|
|
||||||
@@ -120,21 +134,21 @@ class SatsViewModel: ObservableObject {
|
|||||||
satsStringInternal = sats.formatString()
|
satsStringInternal = sats.formatString()
|
||||||
} else {
|
} else {
|
||||||
satsStringInternal = ""
|
satsStringInternal = ""
|
||||||
usdStringInternal = ""
|
currencyValueStringInternal = ""
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
satsStringInternal = ""
|
satsStringInternal = ""
|
||||||
usdStringInternal = ""
|
currencyValueStringInternal = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var btcToUsd: Decimal? {
|
var btcToCurrency: Decimal? {
|
||||||
#if !SKIP
|
#if !SKIP
|
||||||
return Decimal(string: btcToUsdStringInternal)
|
return Decimal(string: btcToCurrencyStringInternal)
|
||||||
#else
|
#else
|
||||||
do {
|
do {
|
||||||
return Decimal(btcToUsdStringInternal)
|
return Decimal(btcToCurrencyStringInternal)
|
||||||
} catch {
|
} catch {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -165,12 +179,12 @@ class SatsViewModel: ObservableObject {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
var usd: Decimal? {
|
var currencyValue: Decimal? {
|
||||||
#if !SKIP
|
#if !SKIP
|
||||||
return Decimal(string: usdStringInternal)
|
return Decimal(string: currencyValueStringInternal)
|
||||||
#else
|
#else
|
||||||
do {
|
do {
|
||||||
return Decimal(usdStringInternal)
|
return Decimal(currencyValueStringInternal)
|
||||||
} catch {
|
} catch {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
// Created by Terry Yiu on 2/19/24.
|
// Created by Terry Yiu on 2/19/24.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
import XCTest
|
import XCTest
|
||||||
@testable import SatsPrice
|
@testable import SatsPrice
|
||||||
|
|
||||||
@@ -15,52 +16,88 @@ final class SatsViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
func testSatsViewModel() {
|
func testSatsViewModel() {
|
||||||
let satsViewModel = SatsViewModel()
|
let satsViewModel = SatsViewModel()
|
||||||
satsViewModel.btcToUsdString = "54321"
|
satsViewModel.btcToCurrencyString = "54321"
|
||||||
|
|
||||||
// Test BTC updates.
|
// Test BTC updates.
|
||||||
satsViewModel.btcString = "1"
|
satsViewModel.btcString = "1"
|
||||||
|
#if !SKIP
|
||||||
XCTAssertEqual(satsViewModel.btc, Decimal(string: "1"))
|
XCTAssertEqual(satsViewModel.btc, Decimal(string: "1"))
|
||||||
XCTAssertEqual(satsViewModel.btcString, "1")
|
|
||||||
XCTAssertEqual(satsViewModel.sats, Decimal(string: "100000000"))
|
XCTAssertEqual(satsViewModel.sats, Decimal(string: "100000000"))
|
||||||
|
XCTAssertEqual(satsViewModel.currencyValue, Decimal(string: "54321"))
|
||||||
|
#else
|
||||||
|
XCTAssertEqual(satsViewModel.btc, Decimal("1"))
|
||||||
|
XCTAssertEqual(satsViewModel.sats, Decimal("100000000"))
|
||||||
|
XCTAssertEqual(satsViewModel.currencyValue, Decimal("54321"))
|
||||||
|
#endif
|
||||||
|
XCTAssertEqual(satsViewModel.btcString, "1")
|
||||||
XCTAssertEqual(satsViewModel.satsString, "100000000")
|
XCTAssertEqual(satsViewModel.satsString, "100000000")
|
||||||
XCTAssertEqual(satsViewModel.usd, Decimal(string: "54321"))
|
XCTAssertEqual(satsViewModel.currencyValueString, "54321")
|
||||||
XCTAssertEqual(satsViewModel.usdString, "54321")
|
|
||||||
|
|
||||||
// Test Sats updates.
|
// Test Sats updates.
|
||||||
satsViewModel.satsString = "200000000"
|
satsViewModel.satsString = "200000000"
|
||||||
|
#if !SKIP
|
||||||
XCTAssertEqual(satsViewModel.btc, Decimal(string: "2"))
|
XCTAssertEqual(satsViewModel.btc, Decimal(string: "2"))
|
||||||
XCTAssertEqual(satsViewModel.btcString, "2")
|
|
||||||
XCTAssertEqual(satsViewModel.sats, Decimal(string: "200000000"))
|
XCTAssertEqual(satsViewModel.sats, Decimal(string: "200000000"))
|
||||||
|
XCTAssertEqual(satsViewModel.currencyValue, Decimal(string: "108642"))
|
||||||
|
#else
|
||||||
|
XCTAssertEqual(satsViewModel.btc, Decimal("2"))
|
||||||
|
XCTAssertEqual(satsViewModel.sats, Decimal("200000000"))
|
||||||
|
XCTAssertEqual(satsViewModel.currencyValue, Decimal("108642"))
|
||||||
|
#endif
|
||||||
|
XCTAssertEqual(satsViewModel.btcString, "2")
|
||||||
XCTAssertEqual(satsViewModel.satsString, "200000000")
|
XCTAssertEqual(satsViewModel.satsString, "200000000")
|
||||||
XCTAssertEqual(satsViewModel.usd, Decimal(string: "108642"))
|
XCTAssertEqual(satsViewModel.currencyValueString, "108642")
|
||||||
XCTAssertEqual(satsViewModel.usdString, "108642")
|
|
||||||
|
|
||||||
// Test USD updates.
|
// Test currency value updates.
|
||||||
satsViewModel.usdString = "162963"
|
satsViewModel.currencyValueString = "162963"
|
||||||
|
#if !SKIP
|
||||||
XCTAssertEqual(satsViewModel.btc, Decimal(string: "3"))
|
XCTAssertEqual(satsViewModel.btc, Decimal(string: "3"))
|
||||||
XCTAssertEqual(satsViewModel.btcString, "3")
|
|
||||||
XCTAssertEqual(satsViewModel.sats, Decimal(string: "300000000"))
|
XCTAssertEqual(satsViewModel.sats, Decimal(string: "300000000"))
|
||||||
|
XCTAssertEqual(satsViewModel.currencyValue, Decimal(string: "162963"))
|
||||||
|
#else
|
||||||
|
XCTAssertEqual(satsViewModel.btc, Decimal("3"))
|
||||||
|
XCTAssertEqual(satsViewModel.sats, Decimal("300000000"))
|
||||||
|
XCTAssertEqual(satsViewModel.currencyValue, Decimal("162963"))
|
||||||
|
#endif
|
||||||
|
XCTAssertEqual(satsViewModel.btcString, "3")
|
||||||
XCTAssertEqual(satsViewModel.satsString, "300000000")
|
XCTAssertEqual(satsViewModel.satsString, "300000000")
|
||||||
XCTAssertEqual(satsViewModel.usd, Decimal(string: "162963"))
|
XCTAssertEqual(satsViewModel.currencyValueString, "162963")
|
||||||
XCTAssertEqual(satsViewModel.usdString, "162963")
|
|
||||||
|
|
||||||
// Test fractional amounts.
|
// Test fractional amounts.
|
||||||
satsViewModel.usdString = "1"
|
// Precision between platforms on this calculation is different so we have different assertions for each.
|
||||||
|
satsViewModel.currencyValueString = "1"
|
||||||
|
#if !SKIP
|
||||||
XCTAssertEqual(satsViewModel.btc, Decimal(string: "0.00001840908672520756245282671526665562"))
|
XCTAssertEqual(satsViewModel.btc, Decimal(string: "0.00001840908672520756245282671526665562"))
|
||||||
XCTAssertEqual(satsViewModel.btcString, "0.00001840908672520756245282671526665562")
|
XCTAssertEqual(satsViewModel.btcString, "0.00001840908672520756245282671526665562")
|
||||||
XCTAssertEqual(satsViewModel.sats, Decimal(string: "1840.908672520756245282671526665562"))
|
XCTAssertEqual(satsViewModel.sats, Decimal(string: "1840.908672520756245282671526665562"))
|
||||||
XCTAssertEqual(satsViewModel.satsString, "1840.908672520756245282671526665562")
|
XCTAssertEqual(satsViewModel.satsString, "1840.908672520756245282671526665562")
|
||||||
XCTAssertEqual(satsViewModel.usd, Decimal(string: "1"))
|
XCTAssertEqual(satsViewModel.currencyValue, Decimal(string: "1"))
|
||||||
XCTAssertEqual(satsViewModel.usdString, "1")
|
#else
|
||||||
|
XCTAssertEqual(satsViewModel.btc, Decimal("0.00001840908672520756"))
|
||||||
|
XCTAssertEqual(satsViewModel.btcString, "0.00001840908672520756")
|
||||||
|
XCTAssertEqual(satsViewModel.sats, Decimal("1840.908672520756"))
|
||||||
|
XCTAssertEqual(satsViewModel.satsString, "1840.908672520756")
|
||||||
|
XCTAssertEqual(satsViewModel.currencyValue, Decimal("1"))
|
||||||
|
#endif
|
||||||
|
XCTAssertEqual(satsViewModel.currencyValueString, "1")
|
||||||
|
|
||||||
// Test large amounts that exceed the cap of 21M BTC.
|
// Test large amounts that exceed the cap of 21M BTC.
|
||||||
satsViewModel.usdString = "11407419999999"
|
// Precision between platforms on this calculation is different so we have different assertions for each.
|
||||||
|
satsViewModel.currencyValueString = "11407419999999"
|
||||||
|
#if !SKIP
|
||||||
XCTAssertEqual(satsViewModel.btc, Decimal(string: "210000184.09084884298889932070469983984"))
|
XCTAssertEqual(satsViewModel.btc, Decimal(string: "210000184.09084884298889932070469983984"))
|
||||||
XCTAssertEqual(satsViewModel.btcString, "210000184.09084884298889932070469983984")
|
XCTAssertEqual(satsViewModel.btcString, "210000184.09084884298889932070469983984")
|
||||||
XCTAssertEqual(satsViewModel.sats, Decimal(string: "21000018409084884.298889932070469983984"))
|
XCTAssertEqual(satsViewModel.sats, Decimal(string: "21000018409084884.298889932070469983984"))
|
||||||
XCTAssertEqual(satsViewModel.satsString, "21000018409084884.298889932070469983984")
|
XCTAssertEqual(satsViewModel.satsString, "21000018409084884.298889932070469983984")
|
||||||
XCTAssertEqual(satsViewModel.usd, Decimal(string: "11407419999999"))
|
XCTAssertEqual(satsViewModel.currencyValue, Decimal(string: "11407419999999"))
|
||||||
XCTAssertEqual(satsViewModel.usdString, "11407419999999")
|
#else
|
||||||
|
XCTAssertEqual(satsViewModel.btc, Decimal("210000184.0908488429888993207"))
|
||||||
|
XCTAssertEqual(satsViewModel.btcString, "210000184.0908488429888993207")
|
||||||
|
XCTAssertEqual(satsViewModel.sats, Decimal("21000018409084884.29888993207"))
|
||||||
|
XCTAssertEqual(satsViewModel.satsString, "21000018409084884.29888993207")
|
||||||
|
XCTAssertEqual(satsViewModel.currencyValue, Decimal("11407419999999"))
|
||||||
|
#endif
|
||||||
|
XCTAssertEqual(satsViewModel.currencyValueString, "11407419999999")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user