Mercurial > public > simoleon
changeset 185:2fc95efcb1ee
connect backend
author | Dennis Concepcion Martin <dennisconcepcionmartin@gmail.com> |
---|---|
date | Wed, 22 Dec 2021 16:12:23 +0100 |
parents | 7cb2b0b2b3f3 |
children | 1ebd1c5dd302 |
files | Simoleon.xcodeproj/project.pbxproj Simoleon/Assets.xcassets/AccentColor.colorset/Contents.json Simoleon/ConversionView.swift Simoleon/Functions/ConditionalWrapper.swift Simoleon/Functions/GetFlagName.swift Simoleon/Functions/HttpRequest.swift Simoleon/Functions/ReadJson.swift Simoleon/Helpers/ConditionalWrapper.swift Simoleon/Helpers/CurrencyBox.swift Simoleon/Helpers/CurrencyConversion.swift Simoleon/Helpers/CurrencyList.swift Simoleon/Helpers/CurrencyRow.swift Simoleon/Helpers/CurrencySelector.swift Simoleon/Helpers/CurrencySelectorLabel.swift Simoleon/Helpers/CurrencyTextfield.swift Simoleon/Helpers/FavoriteButton.swift Simoleon/Helpers/Flag.swift Simoleon/Helpers/ReadJson.swift Simoleon/Models/CurrencyConversionModel.swift SimoleonTests/SimoleonTests.swift |
diffstat | 20 files changed, 559 insertions(+), 151 deletions(-) [+] |
line wrap: on
line diff
--- a/Simoleon.xcodeproj/project.pbxproj Mon Dec 20 12:28:22 2021 +0100 +++ b/Simoleon.xcodeproj/project.pbxproj Wed Dec 22 16:12:23 2021 +0100 @@ -16,16 +16,23 @@ 9511E31B2760B8D7005EEE8D /* SimoleonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9511E31A2760B8D7005EEE8D /* SimoleonTests.swift */; }; 9511E3252760B8D7005EEE8D /* SimoleonUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9511E3242760B8D7005EEE8D /* SimoleonUITests.swift */; }; 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 */; }; + 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 */; }; 954573012760C2030084FFC7 /* ConversionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 954573002760C2030084FFC7 /* ConversionView.swift */; }; 954573042760C2DF0084FFC7 /* ConditionalWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 954573032760C2DF0084FFC7 /* ConditionalWrapper.swift */; }; - 954573062760C4810084FFC7 /* CurrencySelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 954573052760C4810084FFC7 /* CurrencySelector.swift */; }; - 954573082760C4B00084FFC7 /* CurrencyBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 954573072760C4B00084FFC7 /* CurrencyBox.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 */; }; + 95CDF81E2770A082005ED28E /* CurrencySelectorLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95CDF81D2770A082005ED28E /* CurrencySelectorLabel.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -58,11 +65,17 @@ 9511E3202760B8D7005EEE8D /* SimoleonUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SimoleonUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 9511E3242760B8D7005EEE8D /* SimoleonUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimoleonUITests.swift; sourceTree = "<group>"; }; 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>"; }; + 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>"; }; 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>"; }; - 954573052760C4810084FFC7 /* CurrencySelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencySelector.swift; sourceTree = "<group>"; }; - 954573072760C4B00084FFC7 /* CurrencyBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyBox.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>"; }; @@ -70,6 +83,7 @@ 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>"; }; + 95CDF81D2770A082005ED28E /* CurrencySelectorLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencySelectorLabel.swift; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -132,6 +146,7 @@ 954573002760C2030084FFC7 /* ConversionView.swift */, 954573022760C2CE0084FFC7 /* Helpers */, 9545730E2760C8840084FFC7 /* Models */, + 95394FA42770BA7200E4F732 /* Functions */, 954573092760C5DC0084FFC7 /* Resources */, 9511E30A2760B8D7005EEE8D /* Preview Content */, ); @@ -163,14 +178,27 @@ path = SimoleonUITests; sourceTree = "<group>"; }; - 954573022760C2CE0084FFC7 /* Helpers */ = { + 95394FA42770BA7200E4F732 /* Functions */ = { isa = PBXGroup; children = ( 954573032760C2DF0084FFC7 /* ConditionalWrapper.swift */, - 954573052760C4810084FFC7 /* CurrencySelector.swift */, - 954573072760C4B00084FFC7 /* CurrencyBox.swift */, 9545730C2760C77C0084FFC7 /* ReadJson.swift */, + 95394FA52770BA8800E4F732 /* GetFlagName.swift */, + 9518BB662771D26F002C410E /* HttpRequest.swift */, + ); + path = Functions; + sourceTree = "<group>"; + }; + 954573022760C2CE0084FFC7 /* Helpers */ = { + isa = PBXGroup; + children = ( + 95394FA72770BC4100E4F732 /* Flag.swift */, + 95CDF81D2770A082005ED28E /* CurrencySelectorLabel.swift */, 95384AD6277077310045D838 /* CurrencyList.swift */, + 95394FA22770B9D800E4F732 /* CurrencyRow.swift */, + 9518BB5E2771C4B1002C410E /* CurrencyTextfield.swift */, + 9518BB602771CA1D002C410E /* CurrencyConversion.swift */, + 9518BB642771D12D002C410E /* FavoriteButton.swift */, ); path = Helpers; sourceTree = "<group>"; @@ -188,6 +216,7 @@ children = ( 9545730F2760C8980084FFC7 /* SupportedCurrencyModel.swift */, 954573112760C8D60084FFC7 /* SupportedPairModel.swift */, + 9518BB622771CE15002C410E /* CurrencyConversionModel.swift */, ); path = Models; sourceTree = "<group>"; @@ -333,16 +362,23 @@ files = ( 95384AD7277077310045D838 /* CurrencyList.swift in Sources */, 9545730D2760C77C0084FFC7 /* ReadJson.swift in Sources */, + 9518BB672771D26F002C410E /* HttpRequest.swift in Sources */, 954573102760C8980084FFC7 /* SupportedCurrencyModel.swift in Sources */, - 954573082760C4B00084FFC7 /* CurrencyBox.swift in Sources */, + 9518BB652771D12D002C410E /* FavoriteButton.swift in Sources */, 954573042760C2DF0084FFC7 /* ConditionalWrapper.swift in Sources */, + 9518BB612771CA1D002C410E /* CurrencyConversion.swift in Sources */, + 9518BB5F2771C4B1002C410E /* CurrencyTextfield.swift in Sources */, 9511E30E2760B8D7005EEE8D /* Persistence.swift in Sources */, + 95CDF81E2770A082005ED28E /* CurrencySelectorLabel.swift in Sources */, + 95394FA32770B9D800E4F732 /* CurrencyRow.swift in Sources */, + 9518BB632771CE15002C410E /* CurrencyConversionModel.swift in Sources */, 954573122760C8D60084FFC7 /* SupportedPairModel.swift in Sources */, 954573012760C2030084FFC7 /* ConversionView.swift in Sources */, - 954573062760C4810084FFC7 /* CurrencySelector.swift in Sources */, 9511E3072760B8D6005EEE8D /* ContentView.swift in Sources */, 9511E3052760B8D6005EEE8D /* SimoleonApp.swift in Sources */, 9511E3112760B8D7005EEE8D /* Simoleon.xcdatamodeld in Sources */, + 95394FA62770BA8800E4F732 /* GetFlagName.swift in Sources */, + 95394FA82770BC4100E4F732 /* Flag.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };
--- a/Simoleon/Assets.xcassets/AccentColor.colorset/Contents.json Mon Dec 20 12:28:22 2021 +0100 +++ b/Simoleon/Assets.xcassets/AccentColor.colorset/Contents.json Wed Dec 22 16:12:23 2021 +0100 @@ -1,6 +1,15 @@ { "colors" : [ { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.420", + "green" : "0.835", + "red" : "0.388" + } + }, "idiom" : "universal" } ],
--- a/Simoleon/ConversionView.swift Mon Dec 20 12:28:22 2021 +0100 +++ b/Simoleon/ConversionView.swift Wed Dec 22 16:12:23 2021 +0100 @@ -10,23 +10,101 @@ struct ConversionView: View { var showNavigationView: Bool? + // CurrencySelector variables + @State private var baseCurrency = SupportedCurrencyResult(code: "EUR", name: "Euro", isCrypto: 0) + @State private var quoteCurrency = SupportedCurrencyResult(code: "CHF", name: "Swiss Franc", isCrypto: 0) + @State private var showingCurrencyList = false + @State private var selecting: Selection = .baseCurrency + + // CurrencyTextfield variables + @State private var amount = "1" + + // CurrencyConversion variables + @State private var showConversion = false + @State private var conversion = CurrencyConversionResponse(message: [CurrencyConversionResult]()) + var body: some View { ScrollView(showsIndicators: false) { VStack(alignment: .leading, spacing: 20) { + // MARK: - Currency selector HStack { - CurrencySelector() - // FavoriteButton + Button(action: { showCurrencyList(selecting: .baseCurrency) }) { + CurrencySelectorLabel(currency: baseCurrency) + } + + Button(action: { showCurrencyList(selecting: .quoteCurrency)}) { + CurrencySelectorLabel(currency: quoteCurrency) + } + + // MARK: - Favorite button + FavoriteButton() + } + .padding(.bottom) - // ConversionBox + // MARK: - Conversion box + Text("\(baseCurrency.code) - \(baseCurrency.name)") + .font(.callout) + .fontWeight(.semibold) + + CurrencyTextfield(currencyCode: baseCurrency.code, amount: $amount) + .onChange(of: amount) { _ in + showConversion = false + getConversion() + } + + Divider() + Text("\(quoteCurrency.code) - \(quoteCurrency.name)") + .font(.callout) + .fontWeight(.semibold) + + CurrencyConversion( + conversion: conversion, + currencyCode: quoteCurrency.code, + showConversion: $showConversion + ) } .padding() + .sheet(isPresented: $showingCurrencyList) { + CurrencyList(baseCurrency: $baseCurrency, quoteCurrency: $quoteCurrency, selecting: selecting) + } } + .onAppear(perform: getConversion) .navigationTitle("Convert") .if(UIDevice.current.userInterfaceIdiom == .phone && showNavigationView ?? true) { content in NavigationView { content } } } + + // Change selection and show CurrencyList() + private func showCurrencyList(selecting: Selection) { + self.selecting = selecting + showingCurrencyList.toggle() + } + + // 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 { + // Handle exception + } else { + showConversion = true + } + } + } +} + +enum Selection { + case baseCurrency, quoteCurrency } struct ConversionView_Previews: PreviewProvider {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Simoleon/Functions/ConditionalWrapper.swift Wed Dec 22 16:12:23 2021 +0100 @@ -0,0 +1,20 @@ +// +// ConditionalWrapper.swift +// Simoleon +// +// Created by Dennis Concepción Martín on 8/12/21. +// + +import SwiftUI + +// MARK: Wrap view with some content given a condition +extension View { + @ViewBuilder + func `if`<Content: View>(_ conditional: Bool, content: (Self) -> Content) -> some View { + if conditional { + content(self) + } else { + self + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Simoleon/Functions/GetFlagName.swift Wed Dec 22 16:12:23 2021 +0100 @@ -0,0 +1,19 @@ +// +// GetFlagName.swift +// Simoleon +// +// Created by Dennis Concepción Martín on 20/12/21. +// + +import Foundation + +// Given currency code get flag name +func getFlagName(currency: SupportedCurrencyResult) -> String { + guard currency.isCrypto == 0 else { + return "" + } + + let flagName = currency.code.dropLast() + + return String(flagName) +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Simoleon/Functions/HttpRequest.swift Wed Dec 22 16:12:23 2021 +0100 @@ -0,0 +1,35 @@ +// +// HttpRequest.swift +// Simoleon +// +// Created by Dennis Concepción Martín on 21/12/21. +// + +import Foundation + +// Network request +func httpRequest<T: Decodable>(url: String, model: T.Type, completion: @escaping (_ result: T) -> Void) { + guard let url = URL(string: url) else { + print("Invalid URL") + return + } + let request = URLRequest(url: url) + URLSession.shared.dataTask(with: request) { data, response, error in + if let data = data { + do { + // Decode response with the model passed + let decodedResponse = try JSONDecoder().decode(model, from: data) + DispatchQueue.main.async { + completion(decodedResponse) + } + return + } catch { + // Return error regarding the escaping code + print(error) + } + } + // Error with the request + print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")") + } + .resume() +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Simoleon/Functions/ReadJson.swift Wed Dec 22 16:12:23 2021 +0100 @@ -0,0 +1,31 @@ +// +// ReadJson.swift +// Simoleon +// +// Created by Dennis Concepción Martín on 8/12/21. +// + +import Foundation + +// Read JSON file +func readJson<T: Decodable>(from filename: String) -> T { + let data: Data + + guard let file = Bundle.main.url(forResource: filename, withExtension: nil) + else { + fatalError("Failed to locate \(filename)") + } + + do { + data = try Data(contentsOf: file) + } catch { + fatalError("Failed to load \(filename)") + } + + do { + let decoder = JSONDecoder() + return try decoder.decode(T.self, from: data) + } catch { + fatalError("Failed to decode \(filename)") + } +}
--- a/Simoleon/Helpers/ConditionalWrapper.swift Mon Dec 20 12:28:22 2021 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ -// -// ConditionalWrapper.swift -// Simoleon -// -// Created by Dennis Concepción Martín on 8/12/21. -// - -import SwiftUI - -extension View { - @ViewBuilder - func `if`<Content: View>(_ conditional: Bool, content: (Self) -> Content) -> some View { - if conditional { - content(self) - } else { - self - } - } -}
--- a/Simoleon/Helpers/CurrencyBox.swift Mon Dec 20 12:28:22 2021 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ -// -// CurrencyBox.swift -// Simoleon -// -// Created by Dennis Concepción Martín on 8/12/21. -// - -import SwiftUI - -struct CurrencyBox: View { - var body: some View { - RoundedRectangle(cornerRadius: 15) - .foregroundColor(Color(.secondarySystemBackground)) - .frame(height: 60) - .overlay( - HStack { - Image("FLAG_NAME") - .resizable() - .aspectRatio(contentMode: .fill) - .frame(width: 35, height: 35) - .clipShape(Circle()) - } - ) - } -} - -struct CurrencyBox_Previews: PreviewProvider { - static var previews: some View { - CurrencyBox() - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Simoleon/Helpers/CurrencyConversion.swift Wed Dec 22 16:12:23 2021 +0100 @@ -0,0 +1,82 @@ +// +// CurrencyConversion.swift +// Simoleon +// +// Created by Dennis Concepción Martín on 21/12/21. +// + +import SwiftUI + +struct CurrencyConversion: View { + var conversion: CurrencyConversionResponse + var currencyCode: String + @Binding var showConversion: Bool + + var body: some View { + VStack { + RoundedRectangle(cornerRadius: 15) + .frame(height: 60) + .foregroundColor(Color(.secondarySystemBackground)) + .overlay( + VStack { + if showConversion { + let amount = conversion.message.first!.amount + let formattedAmount = format(currency: amount) + Text(formattedAmount) + .font(.title2) + } else { + ProgressView() + } + } + .padding(.leading, 15) + + , alignment: .leading + ) + + if showConversion { + let timestamp = conversion.message.first!.timestamp + Text("Last updated: \(converToDate(epoch: timestamp))") + .font(.caption) + .opacity(0.6) + } + } + } + + // Format conversion to specific currency format + private func format(currency: Double) -> String { + let formatter = NumberFormatter() + formatter.currencyCode = currencyCode + formatter.numberStyle = .currency + + return formatter.string(from: NSNumber(value: currency))! + } + + // COnvert epoch to date + private func converToDate(epoch: Int) -> String { + let dateFormatter = DateFormatter() + dateFormatter.timeStyle = DateFormatter.Style.medium + dateFormatter.dateStyle = DateFormatter.Style.medium + let date = Date(timeIntervalSince1970: TimeInterval(epoch/1000)) + + return dateFormatter.string(from: date) + } +} + +struct CurrencyConversion_Previews: PreviewProvider { + static var previews: some View { + CurrencyConversion( + conversion: + CurrencyConversionResponse( + message: [ + CurrencyConversionResult( + rate: 1.31, + timestamp: 1288282222000, + amount: 95.63 + ) + ] + ), + currencyCode: "CHF", + showConversion: .constant(true) + ) + } +}
--- a/Simoleon/Helpers/CurrencyList.swift Mon Dec 20 12:28:22 2021 +0100 +++ b/Simoleon/Helpers/CurrencyList.swift Wed Dec 22 16:12:23 2021 +0100 @@ -12,20 +12,28 @@ @Binding var baseCurrency: SupportedCurrencyResult @Binding var quoteCurrency: SupportedCurrencyResult var selecting: Selection + @Environment(\.dismiss) var dismiss var body: some View { NavigationView { List { let currencies = getCurrencies() ForEach(currencies, id: \.self) { currency in - Text(currency.code) + CurrencyRow(currency: currency) } } .navigationTitle("Currencies") + .toolbar { + ToolbarItem(placement: .destructiveAction) { + Button(action: { dismiss() }) { + Image(systemName: "multiply.circle.fill") + } + } + } } } - // MARK: - Get compatible currencies given currency code + // Get compatible currencies given currency code func getCurrencies() -> [SupportedCurrencyResult] { let pairs: SupportedPairResponse = readJson(from: "SupportedCurrencies.json") let currencies: SupportedCurrencyResponse = readJson(from: "SupportedCurrencies.json")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Simoleon/Helpers/CurrencyRow.swift Wed Dec 22 16:12:23 2021 +0100 @@ -0,0 +1,38 @@ +// +// CurrencyRow.swift +// Simoleon +// +// Created by Dennis Concepción Martín on 20/12/21. +// + +import SwiftUI + +struct CurrencyRow: View { + var currency: SupportedCurrencyResult + + var body: some View { + HStack { + let flagName = getFlagName(currency: currency) + Flag(flagName: flagName) + + VStack(alignment: .leading) { + Text(currency.code) + .font(.headline) + + Text(currency.name) + .font(.callout) + .opacity(0.6) + } + .padding(.leading) + } + } +} + +struct CurrencyRow_Previews: PreviewProvider { + static var previews: some View { + CurrencyRow( + currency: + SupportedCurrencyResult(code: "EUR", name: "Euro", isCrypto: 0) + ) + } +}
--- a/Simoleon/Helpers/CurrencySelector.swift Mon Dec 20 12:28:22 2021 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -// -// CurrencySelector.swift -// Simoleon -// -// Created by Dennis Concepción Martín on 8/12/21. -// - -import SwiftUI - -enum Selection { - case baseCurrency, quoteCurrency -} - -struct CurrencySelector: View { - @State private var baseCurrency = SupportedCurrencyResult(code: "EUR", name: "Euro", isCrypto: 0) - @State private var quoteCurrency = SupportedCurrencyResult(code: "CHF", name: "Swiss Franc", isCrypto: 0) - @State private var showingCurrencyList = false - @State private var selecting: Selection = .baseCurrency - - var body: some View { - HStack { - Button(action: { - selecting = .baseCurrency - showingCurrencyList.toggle() - - }) { - RoundedRectangle(cornerRadius: 15) - .foregroundColor(Color(.secondarySystemBackground)) - .frame(height: 60) - .overlay( - Text(baseCurrency.code) - ) - } - - Button(action: { - selecting = .quoteCurrency - showingCurrencyList.toggle() - }) { - RoundedRectangle(cornerRadius: 15) - .foregroundColor(Color(.secondarySystemBackground)) - .frame(height: 60) - .overlay( - Text(quoteCurrency.code) - ) - } - } - .sheet(isPresented: $showingCurrencyList) { - CurrencyList(baseCurrency: $baseCurrency, quoteCurrency: $quoteCurrency, selecting: selecting) - } - } -} - -struct CurrencySelector_Previews: PreviewProvider { - static var previews: some View { - CurrencySelector() - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Simoleon/Helpers/CurrencySelectorLabel.swift Wed Dec 22 16:12:23 2021 +0100 @@ -0,0 +1,36 @@ +// +// CurrencySelectorLabel.swift +// Simoleon +// +// Created by Dennis Concepción Martín on 20/12/21. +// + +import SwiftUI + +struct CurrencySelectorLabel: View { + var currency: SupportedCurrencyResult + + var body: some View { + RoundedRectangle(cornerRadius: 15) + .foregroundColor(Color(.secondarySystemBackground)) + .frame(height: 60) + .overlay( + HStack { + let flagName = getFlagName(currency: currency) + Flag(flagName: flagName) + Text(currency.code) + .font(.headline) + .foregroundColor(.primary) + .padding(.leading, 5) + } + ) + } +} + +struct CurrencySelectorLabel_Previews: PreviewProvider { + static var previews: some View { + CurrencySelectorLabel( + currency: SupportedCurrencyResult(code: "EUR", name: "Euro", isCrypto: 0) + ) + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Simoleon/Helpers/CurrencyTextfield.swift Wed Dec 22 16:12:23 2021 +0100 @@ -0,0 +1,33 @@ +// +// CurrencyTextfield.swift +// Simoleon +// +// Created by Dennis Concepción Martín on 21/12/21. +// + +import SwiftUI + +struct CurrencyTextfield: View { + var currencyCode: String + @Binding var amount: String + + var body: some View { + VStack { + TextField("Enter the amount", text: $amount) + .keyboardType(.decimalPad) + .font(.title2) + .padding(15) + .background( + Color(.secondarySystemBackground) + .cornerRadius(15) + .frame(height: 55) + ) + } + } +} + +struct CurrencyTextfield_Previews: PreviewProvider { + static var previews: some View { + CurrencyTextfield(currencyCode: "USD", amount: .constant("1")) + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Simoleon/Helpers/FavoriteButton.swift Wed Dec 22 16:12:23 2021 +0100 @@ -0,0 +1,62 @@ +// +// FavoriteButton.swift +// Simoleon +// +// Created by Dennis Concepción Martín on 21/12/21. +// + +import SwiftUI + +struct FavoriteButton: View { + @State private var scale: CGFloat = 1 + + var body: some View { + Button(action: {}) { + RoundedRectangle(cornerRadius: 15) + .foregroundColor(Color(.secondarySystemBackground)) + .frame(width: 60, height: 60) + .overlay( + VStack { + if isFavorite() { + Image(systemName: "star.fill") + } else { + Image(systemName: "star") + } + } + .font(.system(size: 28)) + .foregroundColor(Color(.systemYellow)) + ) + } + .scaleEffect(scale) + .animation(.linear(duration: 0.2), value: scale) + } + + // Add currency conversion to favorites + private func add() { + + } + + // Remove currency conversion from favorites + private func remove() { + + } + + // Check if currency conversion is in favorites + private func isFavorite() -> Bool { + return false + } + + // Animate favorite button + private func animate() { + scale += 0.2 + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { + scale -= 0.2 + } + } +} + +struct FavoriteButton_Previews: PreviewProvider { + static var previews: some View { + FavoriteButton() + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Simoleon/Helpers/Flag.swift Wed Dec 22 16:12:23 2021 +0100 @@ -0,0 +1,26 @@ +// +// Flag.swift +// Simoleon +// +// Created by Dennis Concepción Martín on 20/12/21. +// + +import SwiftUI + +struct Flag: View { + var flagName: String + + var body: some View { + Image(flagName) + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: 35, height: 35) + .clipShape(Circle()) + } +} + +struct Flag_Previews: PreviewProvider { + static var previews: some View { + Flag(flagName: "EU") + } +}
--- a/Simoleon/Helpers/ReadJson.swift Mon Dec 20 12:28:22 2021 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -// -// ReadJson.swift -// Simoleon -// -// Created by Dennis Concepción Martín on 8/12/21. -// - -import Foundation - -func readJson<T: Decodable>(from filename: String) -> T { - let data: Data - - guard let file = Bundle.main.url(forResource: filename, withExtension: nil) - else { - fatalError("Failed to locate \(filename)") - } - - do { - data = try Data(contentsOf: file) - } catch { - fatalError("Failed to load \(filename)") - } - - do { - let decoder = JSONDecoder() - return try decoder.decode(T.self, from: data) - } catch { - fatalError("Failed to decode \(filename)") - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Simoleon/Models/CurrencyConversionModel.swift Wed Dec 22 16:12:23 2021 +0100 @@ -0,0 +1,18 @@ +// +// 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 +}
--- a/SimoleonTests/SimoleonTests.swift Mon Dec 20 12:28:22 2021 +0100 +++ b/SimoleonTests/SimoleonTests.swift Wed Dec 22 16:12:23 2021 +0100 @@ -43,6 +43,20 @@ let quoteCurrencies = currencyList.getCurrencies() XCTAssertEqual(quoteCurrencies.count, 18, "Quote currencies does not match") } + + func testGetCurrencyFlagName() throws { + let currency = SupportedCurrencyResult(code: "EUR", name: "Euro", isCrypto: 0) + let flagName = getFlagName(currency: currency) + + XCTAssertEqual(flagName, "EU", "Flag name does not match") + } + + func testGetCryptoFlagName() throws { + let currency = SupportedCurrencyResult(code: "BTC", name: "Bitcoin", isCrypto: 1) + let flagName = getFlagName(currency: currency) + + XCTAssertEqual(flagName, "BTC", "Flag name does not match") + } func testPerformanceExample() throws { // This is an example of a performance test case.