# HG changeset patch # User Dennis Concepcion Martin # Date 1630403854 -3600 # Node ID 3913aff613e82f7260c299438047419c367e741f # Parent 0c589138a6f34bb0435dd074c50c5cba01258656 Fix bug that didn't request API on symbol change diff -r 0c589138a6f3 -r 3913aff613e8 Simoleon.xcodeproj/project.pbxproj --- a/Simoleon.xcodeproj/project.pbxproj Sun Aug 29 19:04:34 2021 +0100 +++ b/Simoleon.xcodeproj/project.pbxproj Tue Aug 31 10:57:34 2021 +0100 @@ -20,7 +20,7 @@ 95562D4D26A8962A0047E778 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 95562D4C26A8962A0047E778 /* StoreKit.framework */; }; 95562D5226A8AEF60047E778 /* Purchases in Frameworks */ = {isa = PBXBuildFile; productRef = 95562D5126A8AEF60047E778 /* Purchases */; }; 957065E226A5FE0400523E68 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 957065E126A5FE0400523E68 /* SettingsView.swift */; }; - 957DCF3326D7ADEA00BCAB1E /* CurrencyPair.swift in Sources */ = {isa = PBXBuildFile; fileRef = 957DCF3226D7ADEA00BCAB1E /* CurrencyPair.swift */; }; + 957DCF3326D7ADEA00BCAB1E /* CurrencyConversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 957DCF3226D7ADEA00BCAB1E /* CurrencyConversion.swift */; }; 95851CE326D4DAAE004ADA79 /* CurrencyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95851CE226D4DAAE004ADA79 /* CurrencyButton.swift */; }; 95851CE526D4DB4C004ADA79 /* Flag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95851CE426D4DB4C004ADA79 /* Flag.swift */; }; 9585BB1426A6B7F400E3193E /* NetworkHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9585BB1326A6B7F400E3193E /* NetworkHelper.swift */; }; @@ -125,7 +125,7 @@ 95562D4C26A8962A0047E778 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; }; 956088B526B9307600A4FD6C /* SnapshotHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnapshotHelper.swift; sourceTree = ""; }; 957065E126A5FE0400523E68 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; - 957DCF3226D7ADEA00BCAB1E /* CurrencyPair.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyPair.swift; sourceTree = ""; }; + 957DCF3226D7ADEA00BCAB1E /* CurrencyConversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyConversion.swift; sourceTree = ""; }; 95851CE226D4DAAE004ADA79 /* CurrencyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyButton.swift; sourceTree = ""; }; 95851CE426D4DB4C004ADA79 /* Flag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Flag.swift; sourceTree = ""; }; 9585BB0F26A6B58500E3193E /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; @@ -259,7 +259,7 @@ 95AC820626DAA3ED00CD5C3F /* FavoritePair+CoreDataProperties.swift */, 95AEBCA226A0900E00613729 /* CurrencyQuoteModel.swift */, 953B8B1626D3A970003CF530 /* CurrencyDetailsModel.swift */, - 957DCF3226D7ADEA00BCAB1E /* CurrencyPair.swift */, + 957DCF3226D7ADEA00BCAB1E /* CurrencyConversion.swift */, ); path = Models; sourceTree = ""; @@ -711,7 +711,7 @@ 95D8C8D126A9BC6200BCC188 /* LockedCurrencyPicker.swift in Sources */, 95C517A126A5F6C000BC2B24 /* ResignKeyboard.swift in Sources */, 95CE6A3626D50B7700D9DCBD /* CurrencyList.swift in Sources */, - 957DCF3326D7ADEA00BCAB1E /* CurrencyPair.swift in Sources */, + 957DCF3326D7ADEA00BCAB1E /* CurrencyConversion.swift in Sources */, 95AEBC9D26A04D4600613729 /* CurrencyRow.swift in Sources */, 95AEBCA326A0900E00613729 /* CurrencyQuoteModel.swift in Sources */, 9585BB1426A6B7F400E3193E /* NetworkHelper.swift in Sources */, diff -r 0c589138a6f3 -r 3913aff613e8 Simoleon/ConversionView.swift --- a/Simoleon/ConversionView.swift Sun Aug 29 19:04:34 2021 +0100 +++ b/Simoleon/ConversionView.swift Tue Aug 31 10:57:34 2021 +0100 @@ -10,17 +10,17 @@ struct ConversionView: View { var showNavigationView: Bool? - @StateObject var currencyPair = CurrencyPair() + @StateObject var currencyConversion = CurrencyConversion() var body: some View { ScrollView(showsIndicators: false) { VStack(alignment: .leading, spacing: 20) { HStack { - CurrencySelector(currencyPair: currencyPair) - FavoriteButton(currencyPair: currencyPair) + CurrencySelector(currencyConversion: currencyConversion) + FavoriteButton(currencyConversion: currencyConversion) } - ConversionBox(currencyPair: currencyPair) + ConversionBox(currencyConversion: currencyConversion) .padding(.top) } .padding() diff -r 0c589138a6f3 -r 3913aff613e8 Simoleon/Models/CurrencyConversion.swift --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Simoleon/Models/CurrencyConversion.swift Tue Aug 31 10:57:34 2021 +0100 @@ -0,0 +1,47 @@ +// +// CurrencyConversion.swift +// Simoleon +// +// Created by Dennis Concepción Martín on 26/8/21. +// + +import Foundation + +class CurrencyConversion: ObservableObject { + // Forex pair -> XXX/YYY where XXX = base symbol, YYY = quote symbol + + @Published var baseSymbol = "USD" { + didSet { + getQuote() + } + } + @Published var quoteSymbol = "EUR" { + didSet { + getQuote() + } + } + @Published var quote = CurrencyQuoteModel() + @Published var isShowing = false + @Published var showingAlert = false + + let networkHelper = NetworkHelper() + + init() { + getQuote() + } + + func getQuote() { + self.isShowing = false + let pair = "\(baseSymbol)/\(quoteSymbol)" + let apiKey = readConfig(withKey: "API_KEY")! + let url = "https://api.1forge.com/quotes?pairs=\(pair)&api_key=\(apiKey)" + + try? networkHelper.httpRequest(url: url, model: [CurrencyQuoteModel].self) { response in + if let quote = response.first { + self.quote = quote + } + + self.isShowing = true + } + } +} diff -r 0c589138a6f3 -r 3913aff613e8 Simoleon/Models/CurrencyPair.swift --- a/Simoleon/Models/CurrencyPair.swift Sun Aug 29 19:04:34 2021 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -// -// CurrencyPair.swift -// Simoleon -// -// Created by Dennis Concepción Martín on 26/8/21. -// - -import Foundation - -class CurrencyPair: ObservableObject { - /* - Forex pair -> XXX/YYY - Where XXX is the base currency, and YYY the quote currency - */ - - @Published var baseSymbol = "USD" - @Published var quoteSymbol = "EUR" -} diff -r 0c589138a6f3 -r 3913aff613e8 Simoleon/UI/ConversionBox.swift --- a/Simoleon/UI/ConversionBox.swift Sun Aug 29 19:04:34 2021 +0100 +++ b/Simoleon/UI/ConversionBox.swift Tue Aug 31 10:57:34 2021 +0100 @@ -8,32 +8,29 @@ import SwiftUI struct ConversionBox: View { - @ObservedObject var currencyPair: CurrencyPair + @ObservedObject var currencyConversion: CurrencyConversion @State private var amount = "" @State private var isEditing = false - @State private var showingConversion = false - @State private var currencyQuote = CurrencyQuoteModel() - @State private var showingAlert = false - + let networkHelper = NetworkHelper() let currencyDetails: [String: CurrencyModel] = try! readJson(from: "Currencies.json") var body: some View { VStack(alignment: .leading) { - let baseCurrencyName = currencyDetails[currencyPair.baseSymbol]!.name - Text("\(baseCurrencyName) (\(currencyPair.baseSymbol))") + let baseCurrencyName = currencyDetails[currencyConversion.baseSymbol]!.name + Text("\(baseCurrencyName) (\(currencyConversion.baseSymbol))") .font(.callout) .fontWeight(.semibold) ConversionTextfield(amount: $amount, isEditing: $isEditing) Divider() - - let quoteCurrencyName = currencyDetails[currencyPair.quoteSymbol]!.name - Text("\(quoteCurrencyName) (\(currencyPair.quoteSymbol))") + + let quoteCurrencyName = currencyDetails[currencyConversion.quoteSymbol]!.name + Text("\(quoteCurrencyName) (\(currencyConversion.quoteSymbol))") .font(.callout) .fontWeight(.semibold) - if showingConversion { + if currencyConversion.isShowing { let conversion = convert() Text("\(conversion, specifier: "%.2f")") .font(Font.title.weight(.semibold)) @@ -54,42 +51,18 @@ } } } - - .onAppear { - showingConversion = false - let pair = "\(currencyPair.baseSymbol)/\(currencyPair.quoteSymbol)" - let apiKey = readConfig(withKey: "API_KEY")! - let url = "https://api.1forge.com/quotes?pairs=\(pair)&api_key=\(apiKey)" - try? networkHelper.httpRequest(url: url, model: [CurrencyQuoteModel].self) { response in - if let currencyQuote = response.first { - self.currencyQuote = currencyQuote - } else { - showingAlert = true - } - - showingConversion = true - } - } - .alert(isPresented: $showingAlert) { - Alert( - title: Text("Currencies not supported."), - message: Text("Currently, we are unable to convert from \(currencyPair.baseSymbol) to \(currencyPair.quoteSymbol)."), - dismissButton: .default(Text("Dismiss") - ) - ) - } } private func convert() -> Double { guard let amount = Double(amount) else { return 0 } - guard let price = currencyQuote.price else { return 0 } - + guard let price = currencyConversion.quote.price else { return 0 } + return amount * price } } struct ConversionBox_Previews: PreviewProvider { static var previews: some View { - ConversionBox(currencyPair: CurrencyPair()) + ConversionBox(currencyConversion: CurrencyConversion()) } } diff -r 0c589138a6f3 -r 3913aff613e8 Simoleon/UI/CurrencyList.swift --- a/Simoleon/UI/CurrencyList.swift Sun Aug 29 19:04:34 2021 +0100 +++ b/Simoleon/UI/CurrencyList.swift Tue Aug 31 10:57:34 2021 +0100 @@ -9,8 +9,10 @@ struct CurrencyList: View { var currencies: [String] - @Binding var selectedCurrency: String + var selection: Selection + @ObservedObject var currencyConversion: CurrencyConversion @State private var searchCurrency = "" + @Environment(\.presentationMode) private var presentation let currencyDetails: [String: CurrencyModel] = try! readJson(from: "Currencies.json") @@ -30,7 +32,15 @@ .accessibilityIdentifier("CurrencySearchBar") ForEach(searchResults, id: \.self) { symbol in - Button(action: {selectedCurrency = symbol; presentation.wrappedValue.dismiss()}) { + Button(action: { + if selection == .baseSymbol { + currencyConversion.baseSymbol = symbol + } else { + currencyConversion.quoteSymbol = symbol + } + + presentation.wrappedValue.dismiss() + }) { let currency = currencyDetails[symbol]! CurrencyRow(currency: currency) } @@ -48,6 +58,10 @@ } } } + + enum Selection { + case baseSymbol, quoteSymbol + } } extension View { func listStyle() -> some View { @@ -57,6 +71,6 @@ struct CurrencyList_Previews: PreviewProvider { static var previews: some View { - CurrencyList(currencies: ["USD"], selectedCurrency: .constant("USD")) + CurrencyList(currencies: ["USD"], selection: .baseSymbol, currencyConversion: CurrencyConversion()) } } diff -r 0c589138a6f3 -r 3913aff613e8 Simoleon/UI/CurrencySelector.swift --- a/Simoleon/UI/CurrencySelector.swift Sun Aug 29 19:04:34 2021 +0100 +++ b/Simoleon/UI/CurrencySelector.swift Tue Aug 31 10:57:34 2021 +0100 @@ -8,9 +8,10 @@ import SwiftUI struct CurrencySelector: View { - @ObservedObject var currencyPair: CurrencyPair + @ObservedObject var currencyConversion: CurrencyConversion + @State private var modalSelection: ModalType = .allCurrencies @State private var showingList = false - @State private var modalSelection: ModalType = .allCurrencies + let currencyPairsSupported: [String] = try! readJson(from: "CurrencyPairsSupported.json") private enum ModalType { @@ -23,31 +24,31 @@ modalSelection = .allCurrencies showingList = true }) { - CurrencyButton(selectedCurrency: currencyPair.baseSymbol) + CurrencyButton(selectedCurrency: currencyConversion.baseSymbol) } Button(action: { modalSelection = .compatibleCurrencies showingList = true }) { - CurrencyButton(selectedCurrency: currencyPair.quoteSymbol) + CurrencyButton(selectedCurrency: currencyConversion.quoteSymbol) } } - .onChange(of: currencyPair.baseSymbol) { _ in + .onChange(of: currencyConversion.baseSymbol) { _ in // If the previous quote symbol is not compatible anymore with base symbol // return the first symbol of the new compatible symbols list - let compatibleCurrencies = get(currencyType: .compatible(with: currencyPair.baseSymbol), from: currencyPairsSupported) - if !compatibleCurrencies.contains(currencyPair.quoteSymbol) { - currencyPair.quoteSymbol = compatibleCurrencies.sorted().first! + let compatibleCurrencies = get(currencyType: .compatible(with: currencyConversion.baseSymbol), from: currencyPairsSupported) + if !compatibleCurrencies.contains(currencyConversion.quoteSymbol) { + currencyConversion.quoteSymbol = compatibleCurrencies.sorted().first! } } .sheet(isPresented: $showingList) { if modalSelection == .allCurrencies { let currencies = get(currencyType: .all, from: currencyPairsSupported) - CurrencyList(currencies: currencies, selectedCurrency: $currencyPair.baseSymbol) + CurrencyList(currencies: currencies, selection: .baseSymbol, currencyConversion: currencyConversion) } else { - let currencies = get(currencyType: .compatible(with: currencyPair.baseSymbol), from: currencyPairsSupported) - CurrencyList(currencies: currencies, selectedCurrency: $currencyPair.quoteSymbol) + let currencies = get(currencyType: .compatible(with: currencyConversion.baseSymbol), from: currencyPairsSupported) + CurrencyList(currencies: currencies, selection: .quoteSymbol, currencyConversion: currencyConversion) } } } @@ -84,6 +85,6 @@ struct CurrencySelector_Previews: PreviewProvider { static var previews: some View { - CurrencySelector(currencyPair: CurrencyPair()) + CurrencySelector(currencyConversion: CurrencyConversion()) } } diff -r 0c589138a6f3 -r 3913aff613e8 Simoleon/UI/FavoriteButton.swift --- a/Simoleon/UI/FavoriteButton.swift Sun Aug 29 19:04:34 2021 +0100 +++ b/Simoleon/UI/FavoriteButton.swift Tue Aug 31 10:57:34 2021 +0100 @@ -8,7 +8,7 @@ import SwiftUI struct FavoriteButton: View { - @ObservedObject var currencyPair: CurrencyPair + @ObservedObject var currencyConversion: CurrencyConversion @State private var scale: CGFloat = 1 @Environment(\.managedObjectContext) private var viewContext @FetchRequest(sortDescriptors: []) private var favoritePairs: FetchedResults @@ -43,8 +43,8 @@ func add() { let favoritePair = FavoritePair(context: viewContext) - favoritePair.baseSymbol = currencyPair.baseSymbol - favoritePair.quoteSymbol = currencyPair.quoteSymbol + favoritePair.baseSymbol = currencyConversion.baseSymbol + favoritePair.quoteSymbol = currencyConversion.quoteSymbol do { try viewContext.save() @@ -57,7 +57,7 @@ func remove() { let favoritePair = favoritePairs.first( where: { - $0.baseSymbol == currencyPair.baseSymbol && $0.quoteSymbol == currencyPair.quoteSymbol + $0.baseSymbol == currencyConversion.baseSymbol && $0.quoteSymbol == currencyConversion.quoteSymbol }) viewContext.delete(favoritePair!) @@ -73,7 +73,7 @@ func isFavorite() -> Bool { let favoritePair = favoritePairs.first( where: { - $0.baseSymbol == currencyPair.baseSymbol && $0.quoteSymbol == currencyPair.quoteSymbol + $0.baseSymbol == currencyConversion.baseSymbol && $0.quoteSymbol == currencyConversion.quoteSymbol }) guard let _ = favoritePair else { return false } @@ -91,6 +91,6 @@ struct FavoriteButton_Previews: PreviewProvider { static var previews: some View { - FavoriteButton(currencyPair: CurrencyPair()) + FavoriteButton(currencyConversion: CurrencyConversion()) } } diff -r 0c589138a6f3 -r 3913aff613e8 SimoleonTests/SimoleonTests.swift --- a/SimoleonTests/SimoleonTests.swift Sun Aug 29 19:04:34 2021 +0100 +++ b/SimoleonTests/SimoleonTests.swift Tue Aug 31 10:57:34 2021 +0100 @@ -24,7 +24,7 @@ let expectedResults = [1: ["USD", "EUR"], 2: ["USD"]] // Test - let currencySelector = CurrencySelector(currencyPair: CurrencyPair()) + let currencySelector = CurrencySelector(currencyConversion: CurrencyConversion()) for testCaseNumber in testCases.keys { print("Testing case: \(testCaseNumber)") let mockData = testCases[testCaseNumber]! @@ -41,13 +41,13 @@ let expectedResults = [1: ["GBP"], 2: ["GBP", "EUR"], 3: []] // Test - let currencySelector = CurrencySelector(currencyPair: CurrencyPair()) + let currencySelector = CurrencySelector(currencyConversion: CurrencyConversion()) for testCaseNumber in testCases.keys { print("Testing case: \(testCaseNumber)") let mockData = testCases[testCaseNumber]! let compatibleCurrencies = currencySelector.get( - currencyType: .compatible(with: currencySelector.currencyPair.baseSymbol), from: mockData + currencyType: .compatible(with: currencySelector.currencyConversion.baseSymbol), from: mockData ) // Assert