changeset 187:13d5a8deb6c2

add AboutView and FavoritesView
author Dennis Concepcion Martin <dennisconcepcionmartin@gmail.com>
date Thu, 23 Dec 2021 16:12:22 +0100
parents 1ebd1c5dd302
children e4f5dcf4d596
files Simoleon.xcodeproj/project.pbxproj Simoleon/AboutView.swift Simoleon/ContentView.swift Simoleon/ConversionView.swift Simoleon/FavoritesView.swift Simoleon/Functions/GetFlagName.swift Simoleon/Helpers/CurrencyConversion.swift Simoleon/Helpers/CurrencyList.swift Simoleon/Helpers/CurrencyRow.swift Simoleon/Helpers/CurrencySelectorLabel.swift Simoleon/Helpers/FavoriteButton.swift Simoleon/Helpers/FavoriteRow.swift Simoleon/Helpers/FavoritesPlaceholder.swift Simoleon/Helpers/Flag.swift Simoleon/Helpers/Sidebar.swift SimoleonTests/SimoleonTests.swift
diffstat 16 files changed, 313 insertions(+), 62 deletions(-) [+]
line wrap: on
line diff
--- a/Simoleon.xcodeproj/project.pbxproj	Thu Dec 23 11:30:38 2021 +0100
+++ b/Simoleon.xcodeproj/project.pbxproj	Thu Dec 23 16:12:22 2021 +0100
@@ -23,9 +23,12 @@
 		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 */; };
+		953E20DE27748A7800248325 /* FavoritesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 953E20DD27748A7800248325 /* FavoritesView.swift */; };
+		953E20E027748AC200248325 /* FavoritesPlaceholder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 953E20DF27748AC200248325 /* FavoritesPlaceholder.swift */; };
+		953E20E227748DA600248325 /* FavoriteRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 953E20E127748DA600248325 /* FavoriteRow.swift */; };
+		953E20E42774C3BD00248325 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 953E20E32774C3BD00248325 /* AboutView.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 */; };
@@ -73,9 +76,12 @@
 		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>"; };
+		953E20DD27748A7800248325 /* FavoritesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoritesView.swift; sourceTree = "<group>"; };
+		953E20DF27748AC200248325 /* FavoritesPlaceholder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoritesPlaceholder.swift; sourceTree = "<group>"; };
+		953E20E127748DA600248325 /* FavoriteRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteRow.swift; sourceTree = "<group>"; };
+		953E20E32774C3BD00248325 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.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>"; };
@@ -146,6 +152,8 @@
 				9511E30D2760B8D7005EEE8D /* Persistence.swift */,
 				9511E3062760B8D6005EEE8D /* ContentView.swift */,
 				954573002760C2030084FFC7 /* ConversionView.swift */,
+				953E20DD27748A7800248325 /* FavoritesView.swift */,
+				953E20E32774C3BD00248325 /* AboutView.swift */,
 				954573022760C2CE0084FFC7 /* Helpers */,
 				9545730E2760C8840084FFC7 /* Models */,
 				95394FA42770BA7200E4F732 /* Functions */,
@@ -185,7 +193,6 @@
 			children = (
 				954573032760C2DF0084FFC7 /* ConditionalWrapper.swift */,
 				9545730C2760C77C0084FFC7 /* ReadJson.swift */,
-				95394FA52770BA8800E4F732 /* GetFlagName.swift */,
 				9518BB662771D26F002C410E /* HttpRequest.swift */,
 			);
 			path = Functions;
@@ -202,6 +209,8 @@
 				9518BB5E2771C4B1002C410E /* CurrencyTextfield.swift */,
 				9518BB602771CA1D002C410E /* CurrencyConversion.swift */,
 				9518BB642771D12D002C410E /* FavoriteButton.swift */,
+				953E20DF27748AC200248325 /* FavoritesPlaceholder.swift */,
+				953E20E127748DA600248325 /* FavoriteRow.swift */,
 			);
 			path = Helpers;
 			sourceTree = "<group>";
@@ -366,6 +375,7 @@
 				95384AD7277077310045D838 /* CurrencyList.swift in Sources */,
 				9545730D2760C77C0084FFC7 /* ReadJson.swift in Sources */,
 				9518BB672771D26F002C410E /* HttpRequest.swift in Sources */,
+				953E20E027748AC200248325 /* FavoritesPlaceholder.swift in Sources */,
 				954573102760C8980084FFC7 /* SupportedCurrencyModel.swift in Sources */,
 				9518BB652771D12D002C410E /* FavoriteButton.swift in Sources */,
 				954573042760C2DF0084FFC7 /* ConditionalWrapper.swift in Sources */,
@@ -373,7 +383,10 @@
 				9518BB5F2771C4B1002C410E /* CurrencyTextfield.swift in Sources */,
 				9511E30E2760B8D7005EEE8D /* Persistence.swift in Sources */,
 				95CDF81E2770A082005ED28E /* CurrencySelectorLabel.swift in Sources */,
+				953E20E227748DA600248325 /* FavoriteRow.swift in Sources */,
 				953E20DC277485F200248325 /* Sidebar.swift in Sources */,
+				953E20E42774C3BD00248325 /* AboutView.swift in Sources */,
+				953E20DE27748A7800248325 /* FavoritesView.swift in Sources */,
 				9598983327747D0E00CC4EA3 /* SupportedPairModel.swift in Sources */,
 				95394FA32770B9D800E4F732 /* CurrencyRow.swift in Sources */,
 				9518BB632771CE15002C410E /* CurrencyLatestRateModel.swift in Sources */,
@@ -381,7 +394,6 @@
 				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;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/AboutView.swift	Thu Dec 23 16:12:22 2021 +0100
@@ -0,0 +1,57 @@
+//
+//  AboutView.swift
+//  Simoleon
+//
+//  Created by Dennis Concepción Martín on 23/12/21.
+//
+
+import SwiftUI
+
+struct AboutView: View {
+    var body: some View {
+        Form {
+            let footerString = "This open source app was made by Dennis"
+            Section(footer: Text(footerString)) {
+                Link(destination: URL(string: "https://dennistech.io")!) {
+                    Label {
+                        Text("Web")
+                            .foregroundColor(.primary)
+                    } icon: {
+                        Image(systemName: "safari")
+                            .imageScale(.large)
+                    }
+                }
+                
+                Link(destination: URL(string: "https://twitter.com/dennisconcep")!) {
+                    Label {
+                        Text("Twitter")
+                            .foregroundColor(.primary)
+                    } icon: {
+                        Image(systemName: "link")
+                            .imageScale(.large)
+                    }
+                }
+                
+                Link(destination: URL(string: "https://github.com/denniscm190/simoleon")!) {
+                    Label {
+                        Text("Github")
+                            .foregroundColor(.primary)
+                    } icon: {
+                        Image(systemName: "externaldrive.connected.to.line.below")
+                            .imageScale(.large)
+                    }
+                }
+            }
+        }
+        .navigationTitle("About")
+        .if(UIDevice.current.userInterfaceIdiom == .phone) { content in
+            NavigationView { content }
+        }
+    }
+}
+
+struct AboutView_Previews: PreviewProvider {
+    static var previews: some View {
+        AboutView()
+    }
+}
--- a/Simoleon/ContentView.swift	Thu Dec 23 11:30:38 2021 +0100
+++ b/Simoleon/ContentView.swift	Thu Dec 23 16:12:22 2021 +0100
@@ -18,23 +18,29 @@
         if UIDevice.current.userInterfaceIdiom == .pad {
             NavigationView {
                 Sidebar()
-                ConversionView()
+                ConversionView(
+                    baseCurrency: SupportedCurrencyResult(code: "EUR", name: "Euro", isCrypto: 0),
+                    quoteCurrency: SupportedCurrencyResult(code: "USD", name: "U.S. Dollar", isCrypto: 0)
+                )
             }
         } else {
             TabView(selection: $tab) {
-                ConversionView()
+                ConversionView(
+                    baseCurrency: SupportedCurrencyResult(code: "EUR", name: "Euro", isCrypto: 0),
+                    quoteCurrency: SupportedCurrencyResult(code: "USD", name: "U.S. Dollar", isCrypto: 0)
+                )
                     .tabItem {
                         Label("Convert", systemImage: "arrow.counterclockwise.circle")
                     }
                     .tag(Tab.convert)
                 
-                Text("Favorites View")
+                FavoritesView()
                     .tabItem {
                         Label("Favorites", systemImage: "star")
                     }
                     .tag(Tab.favorites)
 
-                Text("About View")
+                AboutView()
                     .tabItem {
                         Label("About", systemImage: "info.circle")
                     }
--- a/Simoleon/ConversionView.swift	Thu Dec 23 11:30:38 2021 +0100
+++ b/Simoleon/ConversionView.swift	Thu Dec 23 16:12:22 2021 +0100
@@ -11,8 +11,8 @@
     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 var baseCurrency: SupportedCurrencyResult
+    @State var quoteCurrency: SupportedCurrencyResult
     @State private var showingCurrencyList = false
     @State private var selecting: Selection = .baseCurrency
     
@@ -40,7 +40,7 @@
                     }
                     
                     // MARK: - Favorite button
-                    FavoriteButton()
+                    FavoriteButton(baseCurrency: $baseCurrency, quoteCurrency: $quoteCurrency)
                     
                 }
                 .padding(.bottom)
@@ -63,7 +63,7 @@
                     amount: $amount
                 )
             }
-            .padding()
+            .padding(.horizontal)
             .sheet(isPresented: $showingCurrencyList) {
                 CurrencyList(baseCurrency: $baseCurrency, quoteCurrency: $quoteCurrency, selecting: selecting)
             }
@@ -96,7 +96,6 @@
         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 {
@@ -112,6 +111,9 @@
 
 struct ConversionView_Previews: PreviewProvider {
     static var previews: some View {
-        ConversionView(showNavigationView: true)
+        ConversionView(
+            baseCurrency: SupportedCurrencyResult(code: "EUR", name: "Euro", isCrypto: 0),
+            quoteCurrency: SupportedCurrencyResult(code: "USD", name: "U.S. Dollar", isCrypto: 0)
+        )
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/FavoritesView.swift	Thu Dec 23 16:12:22 2021 +0100
@@ -0,0 +1,60 @@
+//
+//  FavoritesView.swift
+//  Simoleon
+//
+//  Created by Dennis Concepción Martín on 23/12/21.
+//
+
+import SwiftUI
+import CoreData
+
+struct FavoritesView: View {
+    @Environment(\.managedObjectContext) private var viewContext
+    @FetchRequest(
+        sortDescriptors: [NSSortDescriptor(keyPath: \FavoritePair.baseCurrency, ascending: true)], animation: .default
+    ) private var favoritePairs: FetchedResults<FavoritePair>
+    
+    var body: some View {
+        VStack {
+            if favoritePairs.isEmpty {
+                FavoritesPlaceholder()
+            } else {
+                List {
+                    ForEach(favoritePairs, id:\.self) { favoritePair in
+                        FavoriteRow(baseCurrency: favoritePair.baseCurrency!, quoteCurrency: favoritePair.quoteCurrency!)
+                    }
+                    .onDelete(perform: remove)
+                }
+            }
+        }
+        .toolbar {
+            #if os(iOS)
+            EditButton()
+            #endif
+        }
+        .navigationTitle("Favorites")
+        .if(UIDevice.current.userInterfaceIdiom == .phone) { content in
+            NavigationView { content }
+        }
+    }
+    
+    // Remove favorite pair from favorites
+    private func remove(offsets: IndexSet) {
+        withAnimation {
+            offsets.map { favoritePairs[$0] }.forEach(viewContext.delete)
+
+            do {
+                try viewContext.save()
+            } catch {
+                let nsError = error as NSError
+                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
+            }
+        }
+    }
+}
+
+struct FavoritesView_Previews: PreviewProvider {
+    static var previews: some View {
+        FavoritesView()
+    }
+}
--- a/Simoleon/Functions/GetFlagName.swift	Thu Dec 23 11:30:38 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-//
-//  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)
-}
--- a/Simoleon/Helpers/CurrencyConversion.swift	Thu Dec 23 11:30:38 2021 +0100
+++ b/Simoleon/Helpers/CurrencyConversion.swift	Thu Dec 23 16:12:22 2021 +0100
@@ -86,7 +86,7 @@
                         )
                     ]
                 ),
-            currencyCode: "CHF",
+            currencyCode: "USD",
             amount: .constant("1")
         )
     }
--- a/Simoleon/Helpers/CurrencyList.swift	Thu Dec 23 11:30:38 2021 +0100
+++ b/Simoleon/Helpers/CurrencyList.swift	Thu Dec 23 16:12:22 2021 +0100
@@ -77,7 +77,7 @@
     static var previews: some View {
         CurrencyList(
             baseCurrency: .constant(SupportedCurrencyResult(code: "EUR", name: "Euro", isCrypto: 0)),
-            quoteCurrency: .constant(SupportedCurrencyResult(code: "CHF", name: "Swiss Franc", isCrypto: 0)),
+            quoteCurrency: .constant(SupportedCurrencyResult(code: "USD", name: "U.S. Dollar", isCrypto: 0)),
             selecting: .baseCurrency
         )
     }
--- a/Simoleon/Helpers/CurrencyRow.swift	Thu Dec 23 11:30:38 2021 +0100
+++ b/Simoleon/Helpers/CurrencyRow.swift	Thu Dec 23 16:12:22 2021 +0100
@@ -12,8 +12,7 @@
     
     var body: some View {
         HStack {
-            let flagName = getFlagName(currency: currency)
-            Flag(flagName: flagName)
+            Flag(currencyCode: currency.code)
             
             VStack(alignment: .leading) {
                 Text(currency.code)
--- a/Simoleon/Helpers/CurrencySelectorLabel.swift	Thu Dec 23 11:30:38 2021 +0100
+++ b/Simoleon/Helpers/CurrencySelectorLabel.swift	Thu Dec 23 16:12:22 2021 +0100
@@ -16,8 +16,7 @@
             .frame(height: 60)
             .overlay(
                 HStack {
-                    let flagName = getFlagName(currency: currency)
-                    Flag(flagName: flagName)
+                    Flag(currencyCode: currency.code)
                     Text(currency.code)
                         .font(.headline)
                         .foregroundColor(.primary)
--- a/Simoleon/Helpers/FavoriteButton.swift	Thu Dec 23 11:30:38 2021 +0100
+++ b/Simoleon/Helpers/FavoriteButton.swift	Thu Dec 23 16:12:22 2021 +0100
@@ -6,12 +6,21 @@
 //
 
 import SwiftUI
+import CoreData
 
 struct FavoriteButton: View {
+    @Binding var baseCurrency: SupportedCurrencyResult
+    @Binding var quoteCurrency: SupportedCurrencyResult
     @State private var scale: CGFloat = 1
+    @Environment(\.managedObjectContext) private var viewContext
+    @FetchRequest(sortDescriptors: []) private var favoritePairs: FetchedResults<FavoritePair>
     
     var body: some View {
-        Button(action: {}) {
+        Button(action: {
+            animate()
+            if isFavorite() { remove() }
+            else { add() }
+        }) {
             RoundedRectangle(cornerRadius: 15)
                 .foregroundColor(Color(.secondarySystemBackground))
                 .frame(width: 60, height: 60)
@@ -33,17 +42,49 @@
     
     // Add currency conversion to favorites
     private func add() {
+        let favoritePair = FavoritePair(context: viewContext)
+        favoritePair.baseCurrency = baseCurrency.code
+        favoritePair.quoteCurrency = quoteCurrency.code
         
+        do {
+            try viewContext.save()
+        } catch {
+            // Replace this implementation with code to handle the error appropriately.
+            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
+            let nsError = error as NSError
+            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
+        }
     }
     
     // Remove currency conversion from favorites
     private func remove() {
+        let favoritePair = favoritePairs.first(
+            where: {
+                $0.baseCurrency == baseCurrency.code && $0.quoteCurrency == quoteCurrency.code
+        })
         
+        viewContext.delete(favoritePair!)
+        
+        do {
+            try viewContext.save()
+        } catch {
+            // Replace this implementation with code to handle the error appropriately.
+            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
+            let nsError = error as NSError
+            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
+        }
     }
     
     // Check if currency conversion is in favorites
     private func isFavorite() -> Bool {
-        return false
+        let favoritePair = favoritePairs.first(
+            where: {
+                $0.baseCurrency == baseCurrency.code && $0.quoteCurrency == quoteCurrency.code
+        })
+        
+        guard let _ = favoritePair else { return false }
+        
+        return true
     }
     
     // Animate favorite button 
@@ -57,6 +98,10 @@
 
 struct FavoriteButton_Previews: PreviewProvider {
     static var previews: some View {
-        FavoriteButton()
+        FavoriteButton(
+            baseCurrency: .constant(SupportedCurrencyResult(code: "EUR", name: "Euro", isCrypto: 0)),
+            quoteCurrency: .constant(SupportedCurrencyResult(code: "USD", name: "U.S. Dollar", isCrypto: 0))
+        )
+            .environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/Helpers/FavoriteRow.swift	Thu Dec 23 16:12:22 2021 +0100
@@ -0,0 +1,65 @@
+//
+//  FavoriteRow.swift
+//  Simoleon
+//
+//  Created by Dennis Concepción Martín on 23/12/21.
+//
+
+import SwiftUI
+
+struct FavoriteRow: View {
+    var baseCurrency: String
+    var quoteCurrency: String
+    
+    var body: some View {
+        let baseCurrencyObject = getCurrencyObject(with: baseCurrency)
+        let quoteCurrencyObject = getCurrencyObject(with: quoteCurrency)
+        NavigationLink(destination:
+            ConversionView(
+                showNavigationView: false,
+                baseCurrency: baseCurrencyObject,
+                quoteCurrency: quoteCurrencyObject
+            )
+        ) {
+            HStack {
+                Flag(currencyCode: baseCurrency)
+                Flag(currencyCode: quoteCurrency)
+                    .offset(x: -25)
+                    .padding(.trailing, -25)
+                
+                let pairObject = getPairObject()
+                VStack(alignment: .leading) {
+                    Text(pairObject.symbol)
+                        .font(.headline)
+                    
+                    Text(pairObject.name)
+                        .font(.callout)
+                        .opacity(0.6)
+                }
+                .padding(.leading)
+            }
+        }
+    }
+    
+    // Get pair object
+    private func getPairObject() -> SupportedPairResult {
+        let pairResponse: SupportedPairResponse = readJson(from: "SupportedCurrencies.json")
+        let pair = pairResponse.pairs.filter { $0.fromCurrency == baseCurrency && $0.toCurrency == quoteCurrency }
+        
+        return pair.first!
+    }
+    
+    // Get currency object
+    private func getCurrencyObject(with currencyCode: String) -> SupportedCurrencyResult {
+        let currencyResponse: SupportedCurrencyResponse = readJson(from: "SupportedCurrencies.json")
+        let currency = currencyResponse.currencies.filter { $0.code == currencyCode }
+        
+        return currency.first!
+    }
+}
+
+struct FavoriteRow_Previews: PreviewProvider {
+    static var previews: some View {
+        FavoriteRow(baseCurrency: "EUR", quoteCurrency: "USD")
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/Helpers/FavoritesPlaceholder.swift	Thu Dec 23 16:12:22 2021 +0100
@@ -0,0 +1,34 @@
+//
+//  FavoritesPlaceholder.swift
+//  Simoleon
+//
+//  Created by Dennis Concepción Martín on 23/12/21.
+//
+
+import SwiftUI
+
+struct FavoritesPlaceholder: View {
+    var body: some View {
+        VStack {
+            Group {
+                Group {
+                    Image(systemName: "star")
+                        .padding(.bottom)
+                    
+                    Text("No Favorites")
+                        .padding(.bottom, 5)
+                }
+                .font(.system(size: 30))
+                
+                Text("Your favorite currencies will appear here.")
+            }
+            .foregroundColor(.secondary)
+        }
+    }
+}
+
+struct FavoritesPlaceholder_Previews: PreviewProvider {
+    static var previews: some View {
+        FavoritesPlaceholder()
+    }
+}
--- a/Simoleon/Helpers/Flag.swift	Thu Dec 23 11:30:38 2021 +0100
+++ b/Simoleon/Helpers/Flag.swift	Thu Dec 23 16:12:22 2021 +0100
@@ -8,10 +8,10 @@
 import SwiftUI
 
 struct Flag: View {
-    var flagName: String
+    var currencyCode: String
     
     var body: some View {
-        Image(flagName)
+        Image(String(currencyCode.dropLast()))
             .resizable()
             .aspectRatio(contentMode: .fill)
             .frame(width: 35, height: 35)
@@ -21,6 +21,6 @@
 
 struct Flag_Previews: PreviewProvider {
     static var previews: some View {
-        Flag(flagName: "EU")
+        Flag(currencyCode: "EUR")
     }
 }
--- a/Simoleon/Helpers/Sidebar.swift	Thu Dec 23 11:30:38 2021 +0100
+++ b/Simoleon/Helpers/Sidebar.swift	Thu Dec 23 16:12:22 2021 +0100
@@ -10,15 +10,20 @@
 struct Sidebar: View {
     var body: some View {
         List {
-            NavigationLink(destination: ConversionView()) {
+            NavigationLink(destination:
+                ConversionView(
+                    baseCurrency: SupportedCurrencyResult(code: "EUR", name: "Euro", isCrypto: 0),
+                    quoteCurrency: SupportedCurrencyResult(code: "USD", name: "U.S. Dollar", isCrypto: 0)
+                )
+            ) {
                 Label("Convert", systemImage: "arrow.counterclockwise.circle")
             }
             
-            NavigationLink(destination: Text("Favorites View")) {
+            NavigationLink(destination: FavoritesView()) {
                 Label("Favorites", systemImage: "star")
             }
             
-            NavigationLink(destination: Text("About")) {
+            NavigationLink(destination: AboutView()) {
                 Label("About", systemImage: "info.circle")
             }
         }
--- a/SimoleonTests/SimoleonTests.swift	Thu Dec 23 11:30:38 2021 +0100
+++ b/SimoleonTests/SimoleonTests.swift	Thu Dec 23 16:12:22 2021 +0100
@@ -43,20 +43,6 @@
         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, "", "Flag name does not match")
-    }
 
     func testPerformanceExample() throws {
         // This is an example of a performance test case.