diff --git a/SatsPrice.xcodeproj/project.pbxproj b/SatsPrice.xcodeproj/project.pbxproj index 49085dc..5173396 100644 --- a/SatsPrice.xcodeproj/project.pbxproj +++ b/SatsPrice.xcodeproj/project.pbxproj @@ -18,11 +18,11 @@ 3AE2D3D52B83E58500DE5F31 /* SatsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D3D42B83E58500DE5F31 /* SatsViewModel.swift */; }; 3AE2D3DA2B83FE5F00DE5F31 /* SatsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D3D92B83FE5F00DE5F31 /* SatsViewModelTests.swift */; }; 3AE2D3DD2B84020200DE5F31 /* BigDecimal in Frameworks */ = {isa = PBXBuildFile; productRef = 3AE2D3DC2B84020200DE5F31 /* BigDecimal */; }; - 3AE2D3DF2B84597100DE5F31 /* SpotPriceFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D3DE2B84597100DE5F31 /* SpotPriceFetcher.swift */; }; - 3AE2D3E12B8459D600DE5F31 /* CoinbaseSpotPriceFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D3E02B8459D600DE5F31 /* CoinbaseSpotPriceFetcher.swift */; }; - 3AE2D3E32B8461FE00DE5F31 /* CoinGeckoSpotPriceFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D3E22B8461FE00DE5F31 /* CoinGeckoSpotPriceFetcher.swift */; }; - 3AE2D3E52B846A8600DE5F31 /* SpotPriceSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D3E42B846A8600DE5F31 /* SpotPriceSource.swift */; }; - 3AE2D3E72B85690200DE5F31 /* SpotPriceFetcherDelegator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D3E62B85690200DE5F31 /* SpotPriceFetcherDelegator.swift */; }; + 3AE2D3DF2B84597100DE5F31 /* PriceFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D3DE2B84597100DE5F31 /* PriceFetcher.swift */; }; + 3AE2D3E12B8459D600DE5F31 /* CoinbasePriceFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D3E02B8459D600DE5F31 /* CoinbasePriceFetcher.swift */; }; + 3AE2D3E32B8461FE00DE5F31 /* CoinGeckoPriceFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D3E22B8461FE00DE5F31 /* CoinGeckoPriceFetcher.swift */; }; + 3AE2D3E52B846A8600DE5F31 /* PriceSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D3E42B846A8600DE5F31 /* PriceSource.swift */; }; + 3AE2D3E72B85690200DE5F31 /* PriceFetcherDelegator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D3E62B85690200DE5F31 /* PriceFetcherDelegator.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -57,11 +57,11 @@ 3AE2D3BF2B83338E00DE5F31 /* SatsPriceUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SatsPriceUITestsLaunchTests.swift; sourceTree = ""; }; 3AE2D3D42B83E58500DE5F31 /* SatsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SatsViewModel.swift; sourceTree = ""; }; 3AE2D3D92B83FE5F00DE5F31 /* SatsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SatsViewModelTests.swift; sourceTree = ""; }; - 3AE2D3DE2B84597100DE5F31 /* SpotPriceFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpotPriceFetcher.swift; sourceTree = ""; }; - 3AE2D3E02B8459D600DE5F31 /* CoinbaseSpotPriceFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoinbaseSpotPriceFetcher.swift; sourceTree = ""; }; - 3AE2D3E22B8461FE00DE5F31 /* CoinGeckoSpotPriceFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoinGeckoSpotPriceFetcher.swift; sourceTree = ""; }; - 3AE2D3E42B846A8600DE5F31 /* SpotPriceSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpotPriceSource.swift; sourceTree = ""; }; - 3AE2D3E62B85690200DE5F31 /* SpotPriceFetcherDelegator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpotPriceFetcherDelegator.swift; sourceTree = ""; }; + 3AE2D3DE2B84597100DE5F31 /* PriceFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PriceFetcher.swift; sourceTree = ""; }; + 3AE2D3E02B8459D600DE5F31 /* CoinbasePriceFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoinbasePriceFetcher.swift; sourceTree = ""; }; + 3AE2D3E22B8461FE00DE5F31 /* CoinGeckoPriceFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoinGeckoPriceFetcher.swift; sourceTree = ""; }; + 3AE2D3E42B846A8600DE5F31 /* PriceSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PriceSource.swift; sourceTree = ""; }; + 3AE2D3E62B85690200DE5F31 /* PriceFetcherDelegator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PriceFetcherDelegator.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -115,9 +115,9 @@ children = ( 3AE2D3E82B856B1800DE5F31 /* Network */, 3AE2D3A32B83338D00DE5F31 /* ContentView.swift */, + 3AE2D3E42B846A8600DE5F31 /* PriceSource.swift */, 3AE2D39C2B83338D00DE5F31 /* SatsPriceApp.swift */, 3AE2D3D42B83E58500DE5F31 /* SatsViewModel.swift */, - 3AE2D3E42B846A8600DE5F31 /* SpotPriceSource.swift */, 3AE2D3A52B83338D00DE5F31 /* Assets.xcassets */, 3AE2D3A72B83338D00DE5F31 /* SatsPrice.entitlements */, 3AE2D3A02B83338D00DE5F31 /* SatsPrice.xcdatamodeld */, @@ -155,10 +155,10 @@ 3AE2D3E82B856B1800DE5F31 /* Network */ = { isa = PBXGroup; children = ( - 3AE2D3E02B8459D600DE5F31 /* CoinbaseSpotPriceFetcher.swift */, - 3AE2D3E22B8461FE00DE5F31 /* CoinGeckoSpotPriceFetcher.swift */, - 3AE2D3DE2B84597100DE5F31 /* SpotPriceFetcher.swift */, - 3AE2D3E62B85690200DE5F31 /* SpotPriceFetcherDelegator.swift */, + 3AE2D3E02B8459D600DE5F31 /* CoinbasePriceFetcher.swift */, + 3AE2D3E22B8461FE00DE5F31 /* CoinGeckoPriceFetcher.swift */, + 3AE2D3DE2B84597100DE5F31 /* PriceFetcher.swift */, + 3AE2D3E62B85690200DE5F31 /* PriceFetcherDelegator.swift */, ); path = Network; sourceTree = ""; @@ -299,15 +299,15 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 3AE2D3E52B846A8600DE5F31 /* SpotPriceSource.swift in Sources */, - 3AE2D3E72B85690200DE5F31 /* SpotPriceFetcherDelegator.swift in Sources */, - 3AE2D3E12B8459D600DE5F31 /* CoinbaseSpotPriceFetcher.swift in Sources */, + 3AE2D3E52B846A8600DE5F31 /* PriceSource.swift in Sources */, + 3AE2D3E72B85690200DE5F31 /* PriceFetcherDelegator.swift in Sources */, + 3AE2D3E12B8459D600DE5F31 /* CoinbasePriceFetcher.swift in Sources */, 3AE2D39D2B83338D00DE5F31 /* SatsPriceApp.swift in Sources */, 3AE2D3A22B83338D00DE5F31 /* SatsPrice.xcdatamodeld in Sources */, 3AE2D3A42B83338D00DE5F31 /* ContentView.swift in Sources */, 3AE2D3D52B83E58500DE5F31 /* SatsViewModel.swift in Sources */, - 3AE2D3E32B8461FE00DE5F31 /* CoinGeckoSpotPriceFetcher.swift in Sources */, - 3AE2D3DF2B84597100DE5F31 /* SpotPriceFetcher.swift in Sources */, + 3AE2D3E32B8461FE00DE5F31 /* CoinGeckoPriceFetcher.swift in Sources */, + 3AE2D3DF2B84597100DE5F31 /* PriceFetcher.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/SatsPrice/ContentView.swift b/SatsPrice/ContentView.swift index a045722..84b1df3 100644 --- a/SatsPrice/ContentView.swift +++ b/SatsPrice/ContentView.swift @@ -12,11 +12,11 @@ import Combine struct ContentView: View { @ObservedObject private var satsViewModel = SatsViewModel() - @State private var spotPriceSource: SpotPriceSource = .coinbase + @State private var priceSource: PriceSource = .coinbase private let dateFormatter = DateFormatter() - private let spotPriceFetcherDelegator = SpotPriceFetcherDelegator() + private let priceFetcherDelegator = PriceFetcherDelegator() init() { dateFormatter.dateStyle = .short @@ -26,7 +26,7 @@ struct ContentView: View { @MainActor func updatePrice() async { do { - guard let price = try await spotPriceFetcherDelegator.btcToUsd() else { + guard let price = try await priceFetcherDelegator.btcToUsd() else { satsViewModel.btcToUsdString = "" return } @@ -41,13 +41,13 @@ struct ContentView: View { var body: some View { Form { Section { - Picker("Price Source", selection: $spotPriceSource) { - ForEach(SpotPriceSource.allCases, id: \.self) { + Picker("Price Source", selection: $priceSource) { + ForEach(PriceSource.allCases, id: \.self) { Text($0.description) } } - .onChange(of: spotPriceSource) { newSpotPriceSource in - spotPriceFetcherDelegator.spotPriceSource = newSpotPriceSource + .onChange(of: priceSource) { newPriceSource in + priceFetcherDelegator.priceSource = newPriceSource Task { await updatePrice() } diff --git a/SatsPrice/Network/CoinGeckoSpotPriceFetcher.swift b/SatsPrice/Network/CoinGeckoPriceFetcher.swift similarity index 53% rename from SatsPrice/Network/CoinGeckoSpotPriceFetcher.swift rename to SatsPrice/Network/CoinGeckoPriceFetcher.swift index 79d9e7b..5cc20fd 100644 --- a/SatsPrice/Network/CoinGeckoSpotPriceFetcher.swift +++ b/SatsPrice/Network/CoinGeckoPriceFetcher.swift @@ -1,5 +1,5 @@ // -// CoinGeckoSpotPriceFetcher.swift +// CoinGeckoPriceFetcher.swift // SatsPrice // // Created by Terry Yiu on 2/19/24. @@ -8,29 +8,29 @@ import Foundation import BigDecimal -private struct CoinGeckoSpotPriceResponse: Codable { - let bitcoin: CoinGeckoSpotPrice +private struct CoinGeckoPriceResponse: Codable { + let bitcoin: CoinGeckoPrice } -private struct CoinGeckoSpotPrice: Codable { +private struct CoinGeckoPrice: Codable { let usd: Decimal } -class CoinGeckoSpotPriceFetcher : SpotPriceFetcher { +class CoinGeckoPriceFetcher : PriceFetcher { private static let urlString = "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd&precision=18" func btcToUsd() async throws -> BigDecimal? { do { - guard let urlComponents = URLComponents(string: CoinGeckoSpotPriceFetcher.urlString), let url = urlComponents.url else { + guard let urlComponents = URLComponents(string: CoinGeckoPriceFetcher.urlString), let url = urlComponents.url else { return nil } let (data, _) = try await URLSession.shared.data(from: url, delegate: nil) - let spotPriceResponse = try JSONDecoder().decode(CoinGeckoSpotPriceResponse.self, from: data) - let spotPrice = spotPriceResponse.bitcoin + let priceResponse = try JSONDecoder().decode(CoinGeckoPriceResponse.self, from: data) + let price = priceResponse.bitcoin - return BigDecimal(spotPrice.usd) + return BigDecimal(price.usd) } catch { return nil } diff --git a/SatsPrice/Network/CoinbaseSpotPriceFetcher.swift b/SatsPrice/Network/CoinbasePriceFetcher.swift similarity index 51% rename from SatsPrice/Network/CoinbaseSpotPriceFetcher.swift rename to SatsPrice/Network/CoinbasePriceFetcher.swift index 4ef94cc..01a2855 100644 --- a/SatsPrice/Network/CoinbaseSpotPriceFetcher.swift +++ b/SatsPrice/Network/CoinbasePriceFetcher.swift @@ -1,5 +1,5 @@ // -// CoinbaseSpotPriceFetcher.swift +// CoinbasePriceFetcher.swift // SatsPrice // // Created by Terry Yiu on 2/19/24. @@ -8,37 +8,37 @@ import Foundation import BigDecimal -private struct CoinbaseSpotPriceResponse: Codable { - let data: CoinbaseSpotPrice +private struct CoinbasePriceResponse: Codable { + let data: CoinbasePrice } -private struct CoinbaseSpotPrice: Codable { +private struct CoinbasePrice: Codable { let amount: String let base: String let currency: String } -class CoinbaseSpotPriceFetcher : SpotPriceFetcher { +class CoinbasePriceFetcher : PriceFetcher { private static let urlString = "https://api.coinbase.com/v2/prices/BTC-USD/spot" private static let btc = "BTC" private static let usd = "USD" func btcToUsd() async throws -> BigDecimal? { do { - guard let urlComponents = URLComponents(string: CoinbaseSpotPriceFetcher.urlString), let url = urlComponents.url else { + guard let urlComponents = URLComponents(string: CoinbasePriceFetcher.urlString), let url = urlComponents.url else { return nil } let (data, _) = try await URLSession.shared.data(from: url, delegate: nil) - let coinbaseSpotPriceResponse = try JSONDecoder().decode(CoinbaseSpotPriceResponse.self, from: data) - let coinbaseSpotPrice = coinbaseSpotPriceResponse.data + let coinbasePriceResponse = try JSONDecoder().decode(CoinbasePriceResponse.self, from: data) + let coinbasePrice = coinbasePriceResponse.data - guard coinbaseSpotPrice.base == CoinbaseSpotPriceFetcher.btc && coinbaseSpotPrice.currency == CoinbaseSpotPriceFetcher.usd else { + guard coinbasePrice.base == CoinbasePriceFetcher.btc && coinbasePrice.currency == CoinbasePriceFetcher.usd else { return nil } - return BigDecimal(coinbaseSpotPrice.amount) + return BigDecimal(coinbasePrice.amount) } catch { return nil } diff --git a/SatsPrice/Network/SpotPriceFetcher.swift b/SatsPrice/Network/PriceFetcher.swift similarity index 72% rename from SatsPrice/Network/SpotPriceFetcher.swift rename to SatsPrice/Network/PriceFetcher.swift index 5aabf4f..341ddc6 100644 --- a/SatsPrice/Network/SpotPriceFetcher.swift +++ b/SatsPrice/Network/PriceFetcher.swift @@ -1,5 +1,5 @@ // -// SpotPriceFetcher.swift +// PriceFetcher.swift // SatsPrice // // Created by Terry Yiu on 2/19/24. @@ -8,6 +8,6 @@ import Foundation import BigDecimal -protocol SpotPriceFetcher { +protocol PriceFetcher { func btcToUsd() async throws -> BigDecimal? } diff --git a/SatsPrice/Network/PriceFetcherDelegator.swift b/SatsPrice/Network/PriceFetcherDelegator.swift new file mode 100644 index 0000000..a60eeaf --- /dev/null +++ b/SatsPrice/Network/PriceFetcherDelegator.swift @@ -0,0 +1,29 @@ +// +// PriceFetcherDelegator.swift +// SatsPrice +// +// Created by Terry Yiu on 2/20/24. +// + +import Foundation +import BigDecimal + +class PriceFetcherDelegator: PriceFetcher { + private let coinbasePriceFetcher = CoinbasePriceFetcher() + private let coinGeckoPriceFetcher = CoinGeckoPriceFetcher() + + var priceSource: PriceSource = .coinbase + + private var delegate: PriceFetcher { + switch priceSource { + case .coinbase: + coinbasePriceFetcher + case .coingecko: + coinGeckoPriceFetcher + } + } + + func btcToUsd() async throws -> BigDecimal? { + return try await delegate.btcToUsd() + } +} diff --git a/SatsPrice/Network/SpotPriceFetcherDelegator.swift b/SatsPrice/Network/SpotPriceFetcherDelegator.swift deleted file mode 100644 index aed6d16..0000000 --- a/SatsPrice/Network/SpotPriceFetcherDelegator.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// SpotPriceFetcherDelegator.swift -// SatsPrice -// -// Created by Terry Yiu on 2/20/24. -// - -import Foundation -import BigDecimal - -class SpotPriceFetcherDelegator: SpotPriceFetcher { - private let coinbaseSpotPriceFetcher = CoinbaseSpotPriceFetcher() - private let coinGeckoSpotPriceFetcher = CoinGeckoSpotPriceFetcher() - - var spotPriceSource: SpotPriceSource = .coinbase - - private var delegate: SpotPriceFetcher { - switch spotPriceSource { - case .coinbase: - coinbaseSpotPriceFetcher - case .coingecko: - coinGeckoSpotPriceFetcher - } - } - - func btcToUsd() async throws -> BigDecimal? { - return try await delegate.btcToUsd() - } -} diff --git a/SatsPrice/SpotPriceSource.swift b/SatsPrice/PriceSource.swift similarity index 76% rename from SatsPrice/SpotPriceSource.swift rename to SatsPrice/PriceSource.swift index 361b6c7..4fdf6c5 100644 --- a/SatsPrice/SpotPriceSource.swift +++ b/SatsPrice/PriceSource.swift @@ -1,5 +1,5 @@ // -// SpotPriceSource.swift +// PriceSource.swift // SatsPrice // // Created by Terry Yiu on 2/20/24. @@ -7,7 +7,7 @@ import Foundation -enum SpotPriceSource: CaseIterable, CustomStringConvertible { +enum PriceSource: CaseIterable, CustomStringConvertible { case coinbase case coingecko