Mercurial > public > simoleon
changeset 186:1ebd1c5dd302
finish ConversionView
author | Dennis Concepcion Martin <dennisconcepcionmartin@gmail.com> |
---|---|
date | Thu, 23 Dec 2021 11:30:38 +0100 |
parents | 2fc95efcb1ee |
children | 13d5a8deb6c2 |
files | Simoleon.xcodeproj/project.pbxproj Simoleon/ContentView.swift Simoleon/ConversionView.swift Simoleon/Helpers/CurrencyConversion.swift Simoleon/Helpers/CurrencyList.swift Simoleon/Helpers/CurrencyRow.swift Simoleon/Helpers/Sidebar.swift Simoleon/Models/CurrencyConversionModel.swift Simoleon/Models/CurrencyLatestRateModel.swift Simoleon/Models/SupportedPairModel.swift Simoleon/Simoleon.xcdatamodeld/Simoleon.xcdatamodel/contents SimoleonTests/SimoleonTests.swift |
diffstat | 12 files changed, 136 insertions(+), 84 deletions(-) [+] |
line wrap: on
line diff
--- a/Simoleon.xcodeproj/project.pbxproj Wed Dec 22 16:12:23 2021 +0100 +++ b/Simoleon.xcodeproj/project.pbxproj Thu Dec 23 11:30:38 2021 +0100 @@ -18,20 +18,21 @@ 9511E3272760B8D7005EEE8D /* SimoleonUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9511E3262760B8D7005EEE8D /* SimoleonUITestsLaunchTests.swift */; }; 9518BB5F2771C4B1002C410E /* CurrencyTextfield.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9518BB5E2771C4B1002C410E /* CurrencyTextfield.swift */; }; 9518BB612771CA1D002C410E /* CurrencyConversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9518BB602771CA1D002C410E /* CurrencyConversion.swift */; }; - 9518BB632771CE15002C410E /* CurrencyConversionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9518BB622771CE15002C410E /* CurrencyConversionModel.swift */; }; + 9518BB632771CE15002C410E /* CurrencyLatestRateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9518BB622771CE15002C410E /* CurrencyLatestRateModel.swift */; }; 9518BB652771D12D002C410E /* FavoriteButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9518BB642771D12D002C410E /* FavoriteButton.swift */; }; 9518BB672771D26F002C410E /* HttpRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9518BB662771D26F002C410E /* HttpRequest.swift */; }; 95384AD7277077310045D838 /* CurrencyList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95384AD6277077310045D838 /* CurrencyList.swift */; }; 95394FA32770B9D800E4F732 /* CurrencyRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95394FA22770B9D800E4F732 /* CurrencyRow.swift */; }; 95394FA62770BA8800E4F732 /* GetFlagName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95394FA52770BA8800E4F732 /* GetFlagName.swift */; }; 95394FA82770BC4100E4F732 /* Flag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95394FA72770BC4100E4F732 /* Flag.swift */; }; + 953E20DC277485F200248325 /* Sidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 953E20DB277485F200248325 /* Sidebar.swift */; }; 954573012760C2030084FFC7 /* ConversionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 954573002760C2030084FFC7 /* ConversionView.swift */; }; 954573042760C2DF0084FFC7 /* ConditionalWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 954573032760C2DF0084FFC7 /* ConditionalWrapper.swift */; }; 9545730B2760C5FC0084FFC7 /* SupportedCurrencies.json in Resources */ = {isa = PBXBuildFile; fileRef = 9545730A2760C5FC0084FFC7 /* SupportedCurrencies.json */; }; 9545730D2760C77C0084FFC7 /* ReadJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9545730C2760C77C0084FFC7 /* ReadJson.swift */; }; 954573102760C8980084FFC7 /* SupportedCurrencyModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9545730F2760C8980084FFC7 /* SupportedCurrencyModel.swift */; }; - 954573122760C8D60084FFC7 /* SupportedPairModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 954573112760C8D60084FFC7 /* SupportedPairModel.swift */; }; 954573162760CE3B0084FFC7 /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 954573152760CE3B0084FFC7 /* CloudKit.framework */; }; + 9598983327747D0E00CC4EA3 /* SupportedPairModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9598983227747D0E00CC4EA3 /* SupportedPairModel.swift */; }; 95CDF81E2770A082005ED28E /* CurrencySelectorLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95CDF81D2770A082005ED28E /* CurrencySelectorLabel.swift */; }; /* End PBXBuildFile section */ @@ -67,22 +68,23 @@ 9511E3262760B8D7005EEE8D /* SimoleonUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimoleonUITestsLaunchTests.swift; sourceTree = "<group>"; }; 9518BB5E2771C4B1002C410E /* CurrencyTextfield.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyTextfield.swift; sourceTree = "<group>"; }; 9518BB602771CA1D002C410E /* CurrencyConversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyConversion.swift; sourceTree = "<group>"; }; - 9518BB622771CE15002C410E /* CurrencyConversionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyConversionModel.swift; sourceTree = "<group>"; }; + 9518BB622771CE15002C410E /* CurrencyLatestRateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyLatestRateModel.swift; sourceTree = "<group>"; }; 9518BB642771D12D002C410E /* FavoriteButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteButton.swift; sourceTree = "<group>"; }; 9518BB662771D26F002C410E /* HttpRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HttpRequest.swift; sourceTree = "<group>"; }; 95384AD6277077310045D838 /* CurrencyList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyList.swift; sourceTree = "<group>"; }; 95394FA22770B9D800E4F732 /* CurrencyRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyRow.swift; sourceTree = "<group>"; }; 95394FA52770BA8800E4F732 /* GetFlagName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetFlagName.swift; sourceTree = "<group>"; }; 95394FA72770BC4100E4F732 /* Flag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Flag.swift; sourceTree = "<group>"; }; + 953E20DB277485F200248325 /* Sidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sidebar.swift; sourceTree = "<group>"; }; 954573002760C2030084FFC7 /* ConversionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversionView.swift; sourceTree = "<group>"; }; 954573032760C2DF0084FFC7 /* ConditionalWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConditionalWrapper.swift; sourceTree = "<group>"; }; 9545730A2760C5FC0084FFC7 /* SupportedCurrencies.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = SupportedCurrencies.json; sourceTree = "<group>"; }; 9545730C2760C77C0084FFC7 /* ReadJson.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadJson.swift; sourceTree = "<group>"; }; 9545730F2760C8980084FFC7 /* SupportedCurrencyModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupportedCurrencyModel.swift; sourceTree = "<group>"; }; - 954573112760C8D60084FFC7 /* SupportedPairModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupportedPairModel.swift; sourceTree = "<group>"; }; 954573132760CE380084FFC7 /* Simoleon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Simoleon.entitlements; sourceTree = "<group>"; }; 954573152760CE3B0084FFC7 /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; }; 954573172760CE490084FFC7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; }; + 9598983227747D0E00CC4EA3 /* SupportedPairModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SupportedPairModel.swift; path = ../../../../.Trash/SupportedPairModel.swift; sourceTree = "<group>"; }; 95CDF81D2770A082005ED28E /* CurrencySelectorLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencySelectorLabel.swift; sourceTree = "<group>"; }; /* End PBXFileReference section */ @@ -192,6 +194,7 @@ 954573022760C2CE0084FFC7 /* Helpers */ = { isa = PBXGroup; children = ( + 953E20DB277485F200248325 /* Sidebar.swift */, 95394FA72770BC4100E4F732 /* Flag.swift */, 95CDF81D2770A082005ED28E /* CurrencySelectorLabel.swift */, 95384AD6277077310045D838 /* CurrencyList.swift */, @@ -215,8 +218,8 @@ isa = PBXGroup; children = ( 9545730F2760C8980084FFC7 /* SupportedCurrencyModel.swift */, - 954573112760C8D60084FFC7 /* SupportedPairModel.swift */, - 9518BB622771CE15002C410E /* CurrencyConversionModel.swift */, + 9598983227747D0E00CC4EA3 /* SupportedPairModel.swift */, + 9518BB622771CE15002C410E /* CurrencyLatestRateModel.swift */, ); path = Models; sourceTree = "<group>"; @@ -370,9 +373,10 @@ 9518BB5F2771C4B1002C410E /* CurrencyTextfield.swift in Sources */, 9511E30E2760B8D7005EEE8D /* Persistence.swift in Sources */, 95CDF81E2770A082005ED28E /* CurrencySelectorLabel.swift in Sources */, + 953E20DC277485F200248325 /* Sidebar.swift in Sources */, + 9598983327747D0E00CC4EA3 /* SupportedPairModel.swift in Sources */, 95394FA32770B9D800E4F732 /* CurrencyRow.swift in Sources */, - 9518BB632771CE15002C410E /* CurrencyConversionModel.swift in Sources */, - 954573122760C8D60084FFC7 /* SupportedPairModel.swift in Sources */, + 9518BB632771CE15002C410E /* CurrencyLatestRateModel.swift in Sources */, 954573012760C2030084FFC7 /* ConversionView.swift in Sources */, 9511E3072760B8D6005EEE8D /* ContentView.swift in Sources */, 9511E3052760B8D6005EEE8D /* SimoleonApp.swift in Sources */,
--- a/Simoleon/ContentView.swift Wed Dec 22 16:12:23 2021 +0100 +++ b/Simoleon/ContentView.swift Thu Dec 23 11:30:38 2021 +0100 @@ -17,7 +17,8 @@ @ViewBuilder var adjustedView: some View { if UIDevice.current.userInterfaceIdiom == .pad { NavigationView { - + Sidebar() + ConversionView() } } else { TabView(selection: $tab) {
--- a/Simoleon/ConversionView.swift Wed Dec 22 16:12:23 2021 +0100 +++ b/Simoleon/ConversionView.swift Thu Dec 23 11:30:38 2021 +0100 @@ -21,7 +21,10 @@ // CurrencyConversion variables @State private var showConversion = false - @State private var conversion = CurrencyConversionResponse(message: [CurrencyConversionResult]()) + @State private var latestRate = CurrencyLatestRateResponse(message: [CurrencyLatestRateResult]()) + + // Update currency rates + @State private var timer = Timer.publish(every: 3, on: .main, in: .common).autoconnect() var body: some View { ScrollView(showsIndicators: false) { @@ -48,10 +51,6 @@ .fontWeight(.semibold) CurrencyTextfield(currencyCode: baseCurrency.code, amount: $amount) - .onChange(of: amount) { _ in - showConversion = false - getConversion() - } Divider() Text("\(quoteCurrency.code) - \(quoteCurrency.name)") @@ -59,9 +58,9 @@ .fontWeight(.semibold) CurrencyConversion( - conversion: conversion, + latestRate: latestRate, currencyCode: quoteCurrency.code, - showConversion: $showConversion + amount: $amount ) } .padding() @@ -69,7 +68,16 @@ CurrencyList(baseCurrency: $baseCurrency, quoteCurrency: $quoteCurrency, selecting: selecting) } } - .onAppear(perform: getConversion) + .onAppear { + getConversion() + timer = Timer.publish(every: 3, on: .main, in: .common).autoconnect() + } + .onReceive(timer) { _ in + getConversion() + } + .onDisappear { + timer.upstream.connect().cancel() + } .navigationTitle("Convert") .if(UIDevice.current.userInterfaceIdiom == .phone && showNavigationView ?? true) { content in NavigationView { content } @@ -84,17 +92,12 @@ // Request conversion private func getConversion() { - guard let amount = Float(amount) else { - amount = "" - showConversion = true - return - } - let currencyPair = "\(baseCurrency.code)\(quoteCurrency.code)" - let url = "https://api.simoleon.app/fx/convert?symbols=\(currencyPair)&amount=\(amount)" - httpRequest(url: url, model: CurrencyConversionResponse.self) { response in - conversion = response - if conversion.message.isEmpty { + let url = "https://api.simoleon.app/fx/latest?symbols=\(currencyPair)" + httpRequest(url: url, model: CurrencyLatestRateResponse.self) { response in + latestRate = response + print(latestRate.message.first!.timestamp) + if latestRate.message.isEmpty { // Handle exception } else { showConversion = true
--- a/Simoleon/Helpers/CurrencyConversion.swift Wed Dec 22 16:12:23 2021 +0100 +++ b/Simoleon/Helpers/CurrencyConversion.swift Thu Dec 23 11:30:38 2021 +0100 @@ -8,9 +8,9 @@ import SwiftUI struct CurrencyConversion: View { - var conversion: CurrencyConversionResponse + var latestRate: CurrencyLatestRateResponse var currencyCode: String - @Binding var showConversion: Bool + @Binding var amount: String var body: some View { VStack { @@ -19,13 +19,13 @@ .foregroundColor(Color(.secondarySystemBackground)) .overlay( VStack { - if showConversion { - let amount = conversion.message.first!.amount - let formattedAmount = format(currency: amount) + if latestRate.message.isEmpty { + ProgressView() + } else { + let conversion = convert(amount: amount) + let formattedAmount = format(currency: conversion) Text(formattedAmount) .font(.title2) - } else { - ProgressView() } } .padding(.leading, 15) @@ -33,8 +33,8 @@ , alignment: .leading ) - if showConversion { - let timestamp = conversion.message.first!.timestamp + if !latestRate.message.isEmpty { + let timestamp = latestRate.message.first!.timestamp Text("Last updated: \(converToDate(epoch: timestamp))") .font(.caption) .opacity(0.6) @@ -51,7 +51,7 @@ return formatter.string(from: NSNumber(value: currency))! } - // COnvert epoch to date + // Convert epoch to date private func converToDate(epoch: Int) -> String { let dateFormatter = DateFormatter() dateFormatter.timeStyle = DateFormatter.Style.medium @@ -60,23 +60,34 @@ return dateFormatter.string(from: date) } + + // Compute conversion + private func convert(amount: String) -> Double { + guard let amount = Double(amount) else { + return Double() + } + + let rate = latestRate.message.first!.rate + let conversion = amount * rate + + return conversion + } } struct CurrencyConversion_Previews: PreviewProvider { static var previews: some View { CurrencyConversion( - conversion: - CurrencyConversionResponse( + latestRate: + CurrencyLatestRateResponse( message: [ - CurrencyConversionResult( + CurrencyLatestRateResult( rate: 1.31, - timestamp: 1288282222000, - amount: 95.63 + timestamp: 1288282222000 ) ] ), currencyCode: "CHF", - showConversion: .constant(true) + amount: .constant("1") ) } }
--- a/Simoleon/Helpers/CurrencyList.swift Wed Dec 22 16:12:23 2021 +0100 +++ b/Simoleon/Helpers/CurrencyList.swift Thu Dec 23 11:30:38 2021 +0100 @@ -19,7 +19,9 @@ List { let currencies = getCurrencies() ForEach(currencies, id: \.self) { currency in - CurrencyRow(currency: currency) + Button(action: { select(currency: currency) }) { + CurrencyRow(currency: currency) + } } } .navigationTitle("Currencies") @@ -58,6 +60,17 @@ return supportedCurrencies } + + // Select currency + private func select(currency: SupportedCurrencyResult) { + if selecting == .baseCurrency { + baseCurrency = currency + } else { + quoteCurrency = currency + } + + dismiss() + } } struct CurrencyList_Previews: PreviewProvider {
--- a/Simoleon/Helpers/CurrencyRow.swift Wed Dec 22 16:12:23 2021 +0100 +++ b/Simoleon/Helpers/CurrencyRow.swift Thu Dec 23 11:30:38 2021 +0100 @@ -23,6 +23,7 @@ .font(.callout) .opacity(0.6) } + .foregroundColor(.primary) .padding(.leading) } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Simoleon/Helpers/Sidebar.swift Thu Dec 23 11:30:38 2021 +0100 @@ -0,0 +1,35 @@ +// +// Sidebar.swift +// Simoleon +// +// Created by Dennis Concepción Martín on 23/12/21. +// + +import SwiftUI + +struct Sidebar: View { + var body: some View { + List { + NavigationLink(destination: ConversionView()) { + Label("Convert", systemImage: "arrow.counterclockwise.circle") + } + + NavigationLink(destination: Text("Favorites View")) { + Label("Favorites", systemImage: "star") + } + + NavigationLink(destination: Text("About")) { + Label("About", systemImage: "info.circle") + } + } + .listStyle(SidebarListStyle()) + .navigationTitle("Categories") + + } +} + +struct Sidebar_Previews: PreviewProvider { + static var previews: some View { + Sidebar() + } +}
--- a/Simoleon/Models/CurrencyConversionModel.swift Wed Dec 22 16:12:23 2021 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -// -// CurrencyConversionModel.swift -// Simoleon -// -// Created by Dennis Concepción Martín on 21/12/21. -// - -import Foundation - -struct CurrencyConversionResponse: Codable { - var message: [CurrencyConversionResult] -} - -struct CurrencyConversionResult: Codable { - var rate: Double - var timestamp: Int - var amount: Double -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Simoleon/Models/CurrencyLatestRateModel.swift Thu Dec 23 11:30:38 2021 +0100 @@ -0,0 +1,17 @@ +// +// CurrencyConversionModel.swift +// Simoleon +// +// Created by Dennis Concepción Martín on 21/12/21. +// + +import Foundation + +struct CurrencyLatestRateResponse: Codable { + var message: [CurrencyLatestRateResult] +} + +struct CurrencyLatestRateResult: Codable { + var rate: Double + var timestamp: Int +}
--- a/Simoleon/Models/SupportedPairModel.swift Wed Dec 22 16:12:23 2021 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -// -// SupportedPairModel.swift -// Simoleon -// -// Created by Dennis Concepción Martín on 8/12/21. -// - -import Foundation - -struct SupportedPairResponse: Codable { - var pairs: [SupportedPairResult] -} - -struct SupportedPairResult: Codable, Hashable { - var fromCurrency: String - var toCurrency: String - var symbol: String - var name: String - var isCrypto: Int -}
--- a/Simoleon/Simoleon.xcdatamodeld/Simoleon.xcdatamodel/contents Wed Dec 22 16:12:23 2021 +0100 +++ b/Simoleon/Simoleon.xcdatamodeld/Simoleon.xcdatamodel/contents Thu Dec 23 11:30:38 2021 +0100 @@ -1,9 +1,14 @@ <?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1" systemVersion="11A491" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="true" userDefinedModelVersionIdentifier=""> +<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="21C52" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier=""> + <entity name="FavoritePair" representedClassName="FavoritePair" syncable="YES" codeGenerationType="class"> + <attribute name="baseCurrency" optional="YES" attributeType="String"/> + <attribute name="quoteCurrency" optional="YES" attributeType="String"/> + </entity> <entity name="Item" representedClassName="Item" syncable="YES" codeGenerationType="class"> <attribute name="timestamp" optional="YES" attributeType="Date" usesScalarValueType="NO"/> </entity> <elements> <element name="Item" positionX="-63" positionY="-18" width="128" height="44"/> + <element name="FavoritePair" positionX="-63" positionY="-9" width="128" height="59"/> </elements> </model> \ No newline at end of file
--- a/SimoleonTests/SimoleonTests.swift Wed Dec 22 16:12:23 2021 +0100 +++ b/SimoleonTests/SimoleonTests.swift Thu Dec 23 11:30:38 2021 +0100 @@ -55,7 +55,7 @@ let currency = SupportedCurrencyResult(code: "BTC", name: "Bitcoin", isCrypto: 1) let flagName = getFlagName(currency: currency) - XCTAssertEqual(flagName, "BTC", "Flag name does not match") + XCTAssertEqual(flagName, "", "Flag name does not match") } func testPerformanceExample() throws {