changeset 156:84137052813d

Refactor code
author Dennis Concepcion Martin <dennisconcepcionmartin@gmail.com>
date Sat, 28 Aug 2021 11:15:25 +0100
parents 681f2cbe8c7f
children 8c3bbd640103
files Simoleon.xcodeproj/project.pbxproj Simoleon.xcodeproj/xcshareddata/xcschemes/Simoleon.xcscheme Simoleon/ContentView.swift Simoleon/Conversion.swift Simoleon/ErrorHandling.swift Simoleon/Favorites.swift Simoleon/FavoritesView.swift Simoleon/Helpers/ConversionBox.swift Simoleon/Helpers/CurrencyRow.swift Simoleon/Helpers/CurrencySelector.swift Simoleon/Helpers/ErrorHandling.swift Simoleon/Helpers/FavoriteButton.swift Simoleon/Helpers/FileHelper.swift Simoleon/Helpers/HapticsHelper.swift Simoleon/Helpers/LockedCurrencyPicker.swift Simoleon/Helpers/NetworkHelper.swift Simoleon/Helpers/Persistence.swift Simoleon/Helpers/RestoreButton.swift Simoleon/Helpers/SearchBar.swift Simoleon/Helpers/Sidebar.swift Simoleon/Helpers/SnapshotHelper.swift Simoleon/Helpers/SubscribeButton.swift Simoleon/Helpers/SubscriptionFeature.swift Simoleon/Jobs/CurrenciesController.swift Simoleon/Jobs/FileController.swift Simoleon/Jobs/HapticsController.swift Simoleon/Jobs/NetworkController.swift Simoleon/Models/CurrencyDetailsModel.swift Simoleon/Models/CurrencyPairModel.swift Simoleon/Models/DefaultCurrency+CoreDataClass.swift Simoleon/Models/DefaultCurrency+CoreDataProperties.swift Simoleon/Models/Favorite+CoreDataClass.swift Simoleon/Models/Favorite+CoreDataProperties.swift Simoleon/Persistence.swift Simoleon/Resources/Currencies.json Simoleon/Resources/CurrencyDetails.json Simoleon/Settings.swift Simoleon/SettingsView.swift Simoleon/Simoleon.xcdatamodeld/Simoleon.xcdatamodel/contents Simoleon/SimoleonApp.swift Simoleon/SubscriptionPaywall.swift Simoleon/Tests/ChildListResets.swift Simoleon/UI/ConversionBox.swift Simoleon/UI/CurrencyButton.swift Simoleon/UI/CurrencyRow.swift Simoleon/UI/FavoriteButton.swift Simoleon/UI/Flag.swift Simoleon/UI/LockedCurrencyPicker.swift Simoleon/UI/RestoreButton.swift Simoleon/UI/SearchBar.swift Simoleon/UI/Sidebar.swift Simoleon/UI/SubscribeButton.swift Simoleon/UI/SubscriptionFeature.swift Simoleon/UI/SubscriptionPaywall.swift fastlane/SnapshotHelper.swift
diffstat 55 files changed, 3184 insertions(+), 2947 deletions(-) [+]
line wrap: on
line diff
--- a/Simoleon.xcodeproj/project.pbxproj	Wed Aug 25 11:00:21 2021 +0100
+++ b/Simoleon.xcodeproj/project.pbxproj	Sat Aug 28 11:15:25 2021 +0100
@@ -8,53 +8,57 @@
 
 /* Begin PBXBuildFile section */
 		950093CA26CBC7A200FEBF67 /* SimoleonAppPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950093C926CBC7A200FEBF67 /* SimoleonAppPreview.swift */; };
-		9501E13826D3A52000D14DE5 /* CurrencyDetails.json in Resources */ = {isa = PBXBuildFile; fileRef = 9501E13726D3A52000D14DE5 /* CurrencyDetails.json */; };
+		9501E13826D3A52000D14DE5 /* Currencies.json in Resources */ = {isa = PBXBuildFile; fileRef = 9501E13726D3A52000D14DE5 /* Currencies.json */; };
 		9501E13A26D3A55200D14DE5 /* CurrencyPairsSupported.json in Resources */ = {isa = PBXBuildFile; fileRef = 9501E13926D3A55200D14DE5 /* CurrencyPairsSupported.json */; };
-		950A377726A820F800CAB175 /* DefaultCurrency+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950A377626A820F400CAB175 /* DefaultCurrency+CoreDataProperties.swift */; };
-		950A377826A820F800CAB175 /* DefaultCurrency+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 950A377526A820F400CAB175 /* DefaultCurrency+CoreDataClass.swift */; };
 		9522BEA926B5A4D20076B098 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9522BEA826B5A4D20076B098 /* AppDelegate.swift */; };
 		9522BEAB26B5AACB0076B098 /* ListModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9522BEAA26B5AACB0076B098 /* ListModifier.swift */; };
 		9522CD9D26CED2E100DD9D03 /* ErrorHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9522CD9C26CED2E100DD9D03 /* ErrorHandling.swift */; };
+		9531D44226D8E4CF00665D2A /* SimoleonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9531D44126D8E4CF00665D2A /* SimoleonTests.swift */; };
 		953B8B1726D3A970003CF530 /* CurrencyDetailsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 953B8B1626D3A970003CF530 /* CurrencyDetailsModel.swift */; };
-		9555933A269B0AB8000FD726 /* Read.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95559339269B0AB8000FD726 /* Read.swift */; };
 		95561E3F26AF25EF00CCB543 /* SubscriptionFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95561E3E26AF25EF00CCB543 /* SubscriptionFeature.swift */; };
 		95562D4D26A8962A0047E778 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 95562D4C26A8962A0047E778 /* StoreKit.framework */; };
 		95562D5226A8AEF60047E778 /* Purchases in Frameworks */ = {isa = PBXBuildFile; productRef = 95562D5126A8AEF60047E778 /* Purchases */; };
-		957065E226A5FE0400523E68 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 957065E126A5FE0400523E68 /* Settings.swift */; };
-		9585BB1226A6B71B00E3193E /* ReadConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9585BB1126A6B71B00E3193E /* ReadConfig.swift */; };
-		9585BB1426A6B7F400E3193E /* NetworkRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9585BB1326A6B7F400E3193E /* NetworkRequest.swift */; };
-		9585BB1A26A6E8FD00E3193E /* SimpleSuccess.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9585BB1926A6E8FD00E3193E /* SimpleSuccess.swift */; };
+		957065E226A5FE0400523E68 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 957065E126A5FE0400523E68 /* SettingsView.swift */; };
+		957DCF3326D7ADEA00BCAB1E /* CurrencyPairModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 957DCF3226D7ADEA00BCAB1E /* CurrencyPairModel.swift */; };
+		95851CE326D4DAAE004ADA79 /* CurrencyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95851CE226D4DAAE004ADA79 /* CurrencyButton.swift */; };
+		95851CE526D4DB4C004ADA79 /* Flag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95851CE426D4DB4C004ADA79 /* Flag.swift */; };
+		95851CE826D4E552004ADA79 /* DefaultCurrency+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95851CE626D4E552004ADA79 /* DefaultCurrency+CoreDataClass.swift */; };
+		95851CE926D4E552004ADA79 /* DefaultCurrency+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95851CE726D4E552004ADA79 /* DefaultCurrency+CoreDataProperties.swift */; };
+		95851CF026D4E89C004ADA79 /* Favorite+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95851CEE26D4E89C004ADA79 /* Favorite+CoreDataClass.swift */; };
+		95851CF126D4E89C004ADA79 /* Favorite+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95851CEF26D4E89C004ADA79 /* Favorite+CoreDataProperties.swift */; };
+		9585BB1426A6B7F400E3193E /* NetworkHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9585BB1326A6B7F400E3193E /* NetworkHelper.swift */; };
+		9585BB1A26A6E8FD00E3193E /* HapticsHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9585BB1926A6E8FD00E3193E /* HapticsHelper.swift */; };
 		95909CB326B07BFC00D051AB /* SearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95909CB226B07BFC00D051AB /* SearchBar.swift */; };
 		959F6DEB26BBD53500101E53 /* SimoleonScreenshots.swift in Sources */ = {isa = PBXBuildFile; fileRef = 959F6DEA26BBD53500101E53 /* SimoleonScreenshots.swift */; };
-		959F6DF326BBD54400101E53 /* SnapshotHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956088B526B9307600A4FD6C /* SnapshotHelper.swift */; };
+		95AA42ED26D78A4A0085570D /* FileHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95AA42EC26D78A4A0085570D /* FileHelper.swift */; };
 		95AEBC9526A03ECB00613729 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95AEBC9426A03ECB00613729 /* ContentView.swift */; };
 		95AEBC9D26A04D4600613729 /* CurrencyRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95AEBC9C26A04D4600613729 /* CurrencyRow.swift */; };
 		95AEBCA326A0900E00613729 /* CurrencyQuoteModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95AEBCA226A0900E00613729 /* CurrencyQuoteModel.swift */; };
-		95B54F4426A4842C001DC0D8 /* Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95B54F4326A4842C001DC0D8 /* Conversion.swift */; };
-		95B54F4626A48852001DC0D8 /* CurrencySelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95B54F4526A48852001DC0D8 /* CurrencySelector.swift */; };
+		95B54F4426A4842C001DC0D8 /* ConversionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95B54F4326A4842C001DC0D8 /* ConversionView.swift */; };
 		95B54F4A26A4A450001DC0D8 /* ConversionBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95B54F4926A4A450001DC0D8 /* ConversionBox.swift */; };
 		95B54F5126A4ACAC001DC0D8 /* Sidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95B54F5026A4ACAC001DC0D8 /* Sidebar.swift */; };
 		95B5F53126AADE4B00BDCE89 /* en.xliff in Resources */ = {isa = PBXBuildFile; fileRef = 95B5F52826AADE4B00BDCE89 /* en.xliff */; };
 		95B5F53226AADE4B00BDCE89 /* contents.json in Resources */ = {isa = PBXBuildFile; fileRef = 95B5F52A26AADE4B00BDCE89 /* contents.json */; };
 		95B5F53326AADE4B00BDCE89 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 95B5F52D26AADE4B00BDCE89 /* Localizable.strings */; };
 		95B5F53426AADE4B00BDCE89 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 95B5F52F26AADE4B00BDCE89 /* InfoPlist.strings */; };
+		95C1DD6A26D8DF9400315C3F /* CurrencySelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C1DD6926D8DF9400315C3F /* CurrencySelector.swift */; };
 		95C5179126A5DC8E00BC2B24 /* ConditionalWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C5179026A5DC8E00BC2B24 /* ConditionalWrapper.swift */; };
 		95C5179926A5EC9F00BC2B24 /* FavoriteButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C5179826A5EC9F00BC2B24 /* FavoriteButton.swift */; };
-		95C5179C26A5EFBE00BC2B24 /* Favorite+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C5179A26A5EFBE00BC2B24 /* Favorite+CoreDataClass.swift */; };
-		95C5179D26A5EFBE00BC2B24 /* Favorite+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C5179B26A5EFBE00BC2B24 /* Favorite+CoreDataProperties.swift */; };
-		95C5179F26A5F34200BC2B24 /* Favorites.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C5179E26A5F34200BC2B24 /* Favorites.swift */; };
+		95C5179F26A5F34200BC2B24 /* FavoritesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C5179E26A5F34200BC2B24 /* FavoritesView.swift */; };
 		95C517A126A5F6C000BC2B24 /* ResignKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C517A026A5F6C000BC2B24 /* ResignKeyboard.swift */; };
 		95C5B2282697752600941585 /* SimoleonApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C5B2272697752600941585 /* SimoleonApp.swift */; };
 		95C5B22C2697752700941585 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 95C5B22B2697752700941585 /* Assets.xcassets */; };
 		95C5B22F2697752700941585 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 95C5B22E2697752700941585 /* Preview Assets.xcassets */; };
 		95C5B2312697752700941585 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C5B2302697752700941585 /* Persistence.swift */; };
 		95C5B2342697752700941585 /* Simoleon.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 95C5B2322697752700941585 /* Simoleon.xcdatamodeld */; };
-		95C5B23F2697752700941585 /* SimoleonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C5B23E2697752700941585 /* SimoleonTests.swift */; };
 		95C5B24A2697752700941585 /* SimoleonUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C5B2492697752700941585 /* SimoleonUITests.swift */; };
+		95CE6A3626D50B7700D9DCBD /* CurrencyList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95CE6A3526D50B7700D9DCBD /* CurrencyList.swift */; };
 		95D8C8C726A95D2900BCC188 /* SubscriptionPaywall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95D8C8C626A95D2900BCC188 /* SubscriptionPaywall.swift */; };
 		95D8C8CD26A9784500BCC188 /* SubscribeButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95D8C8CC26A9784500BCC188 /* SubscribeButton.swift */; };
 		95D8C8CF26A98A7900BCC188 /* RestoreButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95D8C8CE26A98A7900BCC188 /* RestoreButton.swift */; };
 		95D8C8D126A9BC6200BCC188 /* LockedCurrencyPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95D8C8D026A9BC6200BCC188 /* LockedCurrencyPicker.swift */; };
+		95DA4B5626D7D10100566C5E /* SnapshotHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956088B526B9307600A4FD6C /* SnapshotHelper.swift */; };
+		95DA4B5926D7E2DE00566C5E /* ChildListResets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95DA4B5826D7E2DE00566C5E /* ChildListResets.swift */; };
 		95E76436269DFC1A008E9F31 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 95E76435269DFC1A008E9F31 /* LaunchScreen.storyboard */; };
 		95E7643A269E0037008E9F31 /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 95E76439269E0037008E9F31 /* CloudKit.framework */; };
 /* End PBXBuildFile section */
@@ -67,14 +71,14 @@
 			remoteGlobalIDString = 95C5B2232697752600941585;
 			remoteInfo = Simoleon;
 		};
-		959F6DED26BBD53500101E53 /* PBXContainerItemProxy */ = {
+		9531D44426D8E4CF00665D2A /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
 			containerPortal = 95C5B21C2697752600941585 /* Project object */;
 			proxyType = 1;
 			remoteGlobalIDString = 95C5B2232697752600941585;
 			remoteInfo = Simoleon;
 		};
-		95C5B23B2697752700941585 /* PBXContainerItemProxy */ = {
+		959F6DED26BBD53500101E53 /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
 			containerPortal = 95C5B21C2697752600941585 /* Project object */;
 			proxyType = 1;
@@ -107,23 +111,29 @@
 		950093C726CBC7A200FEBF67 /* SimoleonAppPreview.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SimoleonAppPreview.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		950093C926CBC7A200FEBF67 /* SimoleonAppPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimoleonAppPreview.swift; sourceTree = "<group>"; };
 		950093CB26CBC7A200FEBF67 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
-		9501E13726D3A52000D14DE5 /* CurrencyDetails.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = CurrencyDetails.json; sourceTree = "<group>"; };
+		9501E13726D3A52000D14DE5 /* Currencies.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Currencies.json; sourceTree = "<group>"; };
 		9501E13926D3A55200D14DE5 /* CurrencyPairsSupported.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = CurrencyPairsSupported.json; sourceTree = "<group>"; };
-		950A377526A820F400CAB175 /* DefaultCurrency+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DefaultCurrency+CoreDataClass.swift"; sourceTree = "<group>"; };
-		950A377626A820F400CAB175 /* DefaultCurrency+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DefaultCurrency+CoreDataProperties.swift"; sourceTree = "<group>"; };
 		9522BEA826B5A4D20076B098 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
 		9522BEAA26B5AACB0076B098 /* ListModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListModifier.swift; sourceTree = "<group>"; };
 		9522CD9C26CED2E100DD9D03 /* ErrorHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorHandling.swift; sourceTree = "<group>"; };
+		9531D43F26D8E4CF00665D2A /* SimoleonTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SimoleonTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		9531D44126D8E4CF00665D2A /* SimoleonTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimoleonTests.swift; sourceTree = "<group>"; };
+		9531D44326D8E4CF00665D2A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		953B8B1626D3A970003CF530 /* CurrencyDetailsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyDetailsModel.swift; sourceTree = "<group>"; };
-		95559339269B0AB8000FD726 /* Read.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Read.swift; sourceTree = "<group>"; };
 		95561E3E26AF25EF00CCB543 /* SubscriptionFeature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionFeature.swift; sourceTree = "<group>"; };
 		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; name = SnapshotHelper.swift; path = fastlane/SnapshotHelper.swift; sourceTree = "<group>"; };
-		957065E126A5FE0400523E68 /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = "<group>"; };
+		956088B526B9307600A4FD6C /* SnapshotHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnapshotHelper.swift; sourceTree = "<group>"; };
+		957065E126A5FE0400523E68 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
+		957DCF3226D7ADEA00BCAB1E /* CurrencyPairModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyPairModel.swift; sourceTree = "<group>"; };
+		95851CE226D4DAAE004ADA79 /* CurrencyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyButton.swift; sourceTree = "<group>"; };
+		95851CE426D4DB4C004ADA79 /* Flag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Flag.swift; sourceTree = "<group>"; };
+		95851CE626D4E552004ADA79 /* DefaultCurrency+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DefaultCurrency+CoreDataClass.swift"; sourceTree = "<group>"; };
+		95851CE726D4E552004ADA79 /* DefaultCurrency+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DefaultCurrency+CoreDataProperties.swift"; sourceTree = "<group>"; };
+		95851CEE26D4E89C004ADA79 /* Favorite+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Favorite+CoreDataClass.swift"; sourceTree = "<group>"; };
+		95851CEF26D4E89C004ADA79 /* Favorite+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Favorite+CoreDataProperties.swift"; sourceTree = "<group>"; };
 		9585BB0F26A6B58500E3193E /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
-		9585BB1126A6B71B00E3193E /* ReadConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadConfig.swift; sourceTree = "<group>"; };
-		9585BB1326A6B7F400E3193E /* NetworkRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkRequest.swift; sourceTree = "<group>"; };
-		9585BB1926A6E8FD00E3193E /* SimpleSuccess.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleSuccess.swift; sourceTree = "<group>"; };
+		9585BB1326A6B7F400E3193E /* NetworkHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkHelper.swift; sourceTree = "<group>"; };
+		9585BB1926A6E8FD00E3193E /* HapticsHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HapticsHelper.swift; sourceTree = "<group>"; };
 		9587597726B2A59D004086F0 /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/Localizable.strings"; sourceTree = "<group>"; };
 		9587597826B2A59D004086F0 /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
 		9587597926B2A5B6004086F0 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -143,11 +153,11 @@
 		959F6DEA26BBD53500101E53 /* SimoleonScreenshots.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimoleonScreenshots.swift; sourceTree = "<group>"; };
 		959F6DEC26BBD53500101E53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		95A70BE926B0550000CC0273 /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS7.4.sdk/System/Library/Frameworks/CloudKit.framework; sourceTree = DEVELOPER_DIR; };
+		95AA42EC26D78A4A0085570D /* FileHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileHelper.swift; sourceTree = "<group>"; };
 		95AEBC9426A03ECB00613729 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
 		95AEBC9C26A04D4600613729 /* CurrencyRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyRow.swift; sourceTree = "<group>"; };
 		95AEBCA226A0900E00613729 /* CurrencyQuoteModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyQuoteModel.swift; sourceTree = "<group>"; };
-		95B54F4326A4842C001DC0D8 /* Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Conversion.swift; sourceTree = "<group>"; };
-		95B54F4526A48852001DC0D8 /* CurrencySelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencySelector.swift; sourceTree = "<group>"; };
+		95B54F4326A4842C001DC0D8 /* ConversionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversionView.swift; sourceTree = "<group>"; };
 		95B54F4926A4A450001DC0D8 /* ConversionBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversionBox.swift; sourceTree = "<group>"; };
 		95B54F5026A4ACAC001DC0D8 /* Sidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sidebar.swift; sourceTree = "<group>"; };
 		95B5F52826AADE4B00BDCE89 /* en.xliff */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = en.xliff; sourceTree = "<group>"; };
@@ -156,11 +166,10 @@
 		95B5F53026AADE4B00BDCE89 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		95B5F53526AADE5200BDCE89 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
 		95B5F53626AADE5500BDCE89 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+		95C1DD6926D8DF9400315C3F /* CurrencySelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencySelector.swift; sourceTree = "<group>"; };
 		95C5179026A5DC8E00BC2B24 /* ConditionalWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConditionalWrapper.swift; sourceTree = "<group>"; };
 		95C5179826A5EC9F00BC2B24 /* FavoriteButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteButton.swift; sourceTree = "<group>"; };
-		95C5179A26A5EFBE00BC2B24 /* Favorite+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Favorite+CoreDataClass.swift"; sourceTree = "<group>"; };
-		95C5179B26A5EFBE00BC2B24 /* Favorite+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Favorite+CoreDataProperties.swift"; sourceTree = "<group>"; };
-		95C5179E26A5F34200BC2B24 /* Favorites.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Favorites.swift; sourceTree = "<group>"; };
+		95C5179E26A5F34200BC2B24 /* FavoritesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoritesView.swift; sourceTree = "<group>"; };
 		95C517A026A5F6C000BC2B24 /* ResignKeyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResignKeyboard.swift; sourceTree = "<group>"; };
 		95C5B2242697752600941585 /* Simoleon.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Simoleon.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		95C5B2272697752600941585 /* SimoleonApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimoleonApp.swift; sourceTree = "<group>"; };
@@ -169,16 +178,15 @@
 		95C5B2302697752700941585 /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
 		95C5B2332697752700941585 /* Simoleon.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Simoleon.xcdatamodel; sourceTree = "<group>"; };
 		95C5B2352697752700941585 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
-		95C5B23A2697752700941585 /* SimoleonTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SimoleonTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
-		95C5B23E2697752700941585 /* SimoleonTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimoleonTests.swift; sourceTree = "<group>"; };
-		95C5B2402697752700941585 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		95C5B2452697752700941585 /* SimoleonUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SimoleonUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		95C5B2492697752700941585 /* SimoleonUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimoleonUITests.swift; sourceTree = "<group>"; };
 		95C5B24B2697752700941585 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		95CE6A3526D50B7700D9DCBD /* CurrencyList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyList.swift; sourceTree = "<group>"; };
 		95D8C8C626A95D2900BCC188 /* SubscriptionPaywall.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionPaywall.swift; sourceTree = "<group>"; };
 		95D8C8CC26A9784500BCC188 /* SubscribeButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscribeButton.swift; sourceTree = "<group>"; };
 		95D8C8CE26A98A7900BCC188 /* RestoreButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestoreButton.swift; sourceTree = "<group>"; };
 		95D8C8D026A9BC6200BCC188 /* LockedCurrencyPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockedCurrencyPicker.swift; sourceTree = "<group>"; };
+		95DA4B5826D7E2DE00566C5E /* ChildListResets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChildListResets.swift; sourceTree = "<group>"; };
 		95E76435269DFC1A008E9F31 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
 		95E76437269E0033008E9F31 /* Simoleon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Simoleon.entitlements; sourceTree = "<group>"; };
 		95E76439269E0037008E9F31 /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; };
@@ -192,6 +200,13 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		9531D43C26D8E4CF00665D2A /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		959F6DE526BBD53500101E53 /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
@@ -209,13 +224,6 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
-		95C5B2372697752700941585 /* Frameworks */ = {
-			isa = PBXFrameworksBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
 		95C5B2422697752700941585 /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
@@ -235,34 +243,33 @@
 			path = SimoleonAppPreview;
 			sourceTree = "<group>";
 		};
+		9531D44026D8E4CF00665D2A /* SimoleonTests */ = {
+			isa = PBXGroup;
+			children = (
+				9531D44126D8E4CF00665D2A /* SimoleonTests.swift */,
+				9531D44326D8E4CF00665D2A /* Info.plist */,
+			);
+			path = SimoleonTests;
+			sourceTree = "<group>";
+		};
 		95559331269B094A000FD726 /* Models */ = {
 			isa = PBXGroup;
 			children = (
-				950A377526A820F400CAB175 /* DefaultCurrency+CoreDataClass.swift */,
-				950A377626A820F400CAB175 /* DefaultCurrency+CoreDataProperties.swift */,
-				95C5179A26A5EFBE00BC2B24 /* Favorite+CoreDataClass.swift */,
-				95C5179B26A5EFBE00BC2B24 /* Favorite+CoreDataProperties.swift */,
+				95851CEE26D4E89C004ADA79 /* Favorite+CoreDataClass.swift */,
+				95851CEF26D4E89C004ADA79 /* Favorite+CoreDataProperties.swift */,
+				95851CE626D4E552004ADA79 /* DefaultCurrency+CoreDataClass.swift */,
+				95851CE726D4E552004ADA79 /* DefaultCurrency+CoreDataProperties.swift */,
 				95AEBCA226A0900E00613729 /* CurrencyQuoteModel.swift */,
 				953B8B1626D3A970003CF530 /* CurrencyDetailsModel.swift */,
+				957DCF3226D7ADEA00BCAB1E /* CurrencyPairModel.swift */,
 			);
 			path = Models;
 			sourceTree = "<group>";
 		};
-		95559338269B0AAA000FD726 /* Functions */ = {
-			isa = PBXGroup;
-			children = (
-				95559339269B0AB8000FD726 /* Read.swift */,
-				9585BB1126A6B71B00E3193E /* ReadConfig.swift */,
-				9585BB1326A6B7F400E3193E /* NetworkRequest.swift */,
-				9585BB1926A6E8FD00E3193E /* SimpleSuccess.swift */,
-			);
-			path = Functions;
-			sourceTree = "<group>";
-		};
 		9555933B269B0DF9000FD726 /* Resources */ = {
 			isa = PBXGroup;
 			children = (
-				9501E13726D3A52000D14DE5 /* CurrencyDetails.json */,
+				9501E13726D3A52000D14DE5 /* Currencies.json */,
 				9501E13926D3A55200D14DE5 /* CurrencyPairsSupported.json */,
 			);
 			path = Resources;
@@ -323,9 +330,8 @@
 		95C5B21B2697752600941585 = {
 			isa = PBXGroup;
 			children = (
-				956088B526B9307600A4FD6C /* SnapshotHelper.swift */,
 				95C5B2262697752600941585 /* Simoleon */,
-				95C5B23D2697752700941585 /* SimoleonTests */,
+				9531D44026D8E4CF00665D2A /* SimoleonTests */,
 				95C5B2482697752700941585 /* SimoleonUITests */,
 				959F6DE926BBD53500101E53 /* SimoleonScreenshots */,
 				950093C826CBC7A200FEBF67 /* SimoleonAppPreview */,
@@ -338,10 +344,10 @@
 			isa = PBXGroup;
 			children = (
 				95C5B2242697752600941585 /* Simoleon.app */,
-				95C5B23A2697752700941585 /* SimoleonTests.xctest */,
 				95C5B2452697752700941585 /* SimoleonUITests.xctest */,
 				959F6DE826BBD53500101E53 /* SimoleonScreenshots.xctest */,
 				950093C726CBC7A200FEBF67 /* SimoleonAppPreview.xctest */,
+				9531D43F26D8E4CF00665D2A /* SimoleonTests.xctest */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -351,25 +357,23 @@
 			children = (
 				95E76437269E0033008E9F31 /* Simoleon.entitlements */,
 				9585BB0F26A6B58500E3193E /* Config.xcconfig */,
+				95C5B22B2697752700941585 /* Assets.xcassets */,
+				95C5B2352697752700941585 /* Info.plist */,
+				95C5B2322697752700941585 /* Simoleon.xcdatamodeld */,
+				95E76435269DFC1A008E9F31 /* LaunchScreen.storyboard */,
 				9522BEA826B5A4D20076B098 /* AppDelegate.swift */,
 				95C5B2272697752600941585 /* SimoleonApp.swift */,
 				95AEBC9426A03ECB00613729 /* ContentView.swift */,
-				95B54F4326A4842C001DC0D8 /* Conversion.swift */,
-				95C5179E26A5F34200BC2B24 /* Favorites.swift */,
-				957065E126A5FE0400523E68 /* Settings.swift */,
-				95D8C8C626A95D2900BCC188 /* SubscriptionPaywall.swift */,
-				95C5B22B2697752700941585 /* Assets.xcassets */,
-				95C5B2302697752700941585 /* Persistence.swift */,
-				9522CD9C26CED2E100DD9D03 /* ErrorHandling.swift */,
-				95C5B2352697752700941585 /* Info.plist */,
-				95C5B2322697752700941585 /* Simoleon.xcdatamodeld */,
-				95E76435269DFC1A008E9F31 /* LaunchScreen.storyboard */,
+				95B54F4326A4842C001DC0D8 /* ConversionView.swift */,
+				95C5179E26A5F34200BC2B24 /* FavoritesView.swift */,
+				957065E126A5FE0400523E68 /* SettingsView.swift */,
+				95DA4B5226D7D09A00566C5E /* UI */,
 				95FE659A269AFB44008745DE /* Helpers */,
 				95559331269B094A000FD726 /* Models */,
-				95559338269B0AAA000FD726 /* Functions */,
 				9555933B269B0DF9000FD726 /* Resources */,
 				95B5F52526AADE4B00BDCE89 /* Localization */,
 				95C5B22D2697752700941585 /* Preview Content */,
+				95DA4B5726D7E2B400566C5E /* Tests */,
 			);
 			path = Simoleon;
 			sourceTree = "<group>";
@@ -382,15 +386,6 @@
 			path = "Preview Content";
 			sourceTree = "<group>";
 		};
-		95C5B23D2697752700941585 /* SimoleonTests */ = {
-			isa = PBXGroup;
-			children = (
-				95C5B23E2697752700941585 /* SimoleonTests.swift */,
-				95C5B2402697752700941585 /* Info.plist */,
-			);
-			path = SimoleonTests;
-			sourceTree = "<group>";
-		};
 		95C5B2482697752700941585 /* SimoleonUITests */ = {
 			isa = PBXGroup;
 			children = (
@@ -400,6 +395,35 @@
 			path = SimoleonUITests;
 			sourceTree = "<group>";
 		};
+		95DA4B5226D7D09A00566C5E /* UI */ = {
+			isa = PBXGroup;
+			children = (
+				95C1DD6926D8DF9400315C3F /* CurrencySelector.swift */,
+				95851CE226D4DAAE004ADA79 /* CurrencyButton.swift */,
+				95CE6A3526D50B7700D9DCBD /* CurrencyList.swift */,
+				95C5179826A5EC9F00BC2B24 /* FavoriteButton.swift */,
+				95B54F4926A4A450001DC0D8 /* ConversionBox.swift */,
+				95AEBC9C26A04D4600613729 /* CurrencyRow.swift */,
+				95851CE426D4DB4C004ADA79 /* Flag.swift */,
+				95B54F5026A4ACAC001DC0D8 /* Sidebar.swift */,
+				95909CB226B07BFC00D051AB /* SearchBar.swift */,
+				95D8C8D026A9BC6200BCC188 /* LockedCurrencyPicker.swift */,
+				95D8C8C626A95D2900BCC188 /* SubscriptionPaywall.swift */,
+				95D8C8CC26A9784500BCC188 /* SubscribeButton.swift */,
+				95D8C8CE26A98A7900BCC188 /* RestoreButton.swift */,
+				95561E3E26AF25EF00CCB543 /* SubscriptionFeature.swift */,
+			);
+			path = UI;
+			sourceTree = "<group>";
+		};
+		95DA4B5726D7E2B400566C5E /* Tests */ = {
+			isa = PBXGroup;
+			children = (
+				95DA4B5826D7E2DE00566C5E /* ChildListResets.swift */,
+			);
+			path = Tests;
+			sourceTree = "<group>";
+		};
 		95E76438269E0037008E9F31 /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
@@ -413,19 +437,15 @@
 		95FE659A269AFB44008745DE /* Helpers */ = {
 			isa = PBXGroup;
 			children = (
-				95B54F4526A48852001DC0D8 /* CurrencySelector.swift */,
-				95AEBC9C26A04D4600613729 /* CurrencyRow.swift */,
-				95B54F4926A4A450001DC0D8 /* ConversionBox.swift */,
-				95B54F5026A4ACAC001DC0D8 /* Sidebar.swift */,
+				95C5B2302697752700941585 /* Persistence.swift */,
+				9522CD9C26CED2E100DD9D03 /* ErrorHandling.swift */,
+				9585BB1326A6B7F400E3193E /* NetworkHelper.swift */,
+				9585BB1926A6E8FD00E3193E /* HapticsHelper.swift */,
+				95AA42EC26D78A4A0085570D /* FileHelper.swift */,
+				956088B526B9307600A4FD6C /* SnapshotHelper.swift */,
 				95C5179026A5DC8E00BC2B24 /* ConditionalWrapper.swift */,
-				95C5179826A5EC9F00BC2B24 /* FavoriteButton.swift */,
+				9522BEAA26B5AACB0076B098 /* ListModifier.swift */,
 				95C517A026A5F6C000BC2B24 /* ResignKeyboard.swift */,
-				95D8C8CC26A9784500BCC188 /* SubscribeButton.swift */,
-				95D8C8CE26A98A7900BCC188 /* RestoreButton.swift */,
-				95D8C8D026A9BC6200BCC188 /* LockedCurrencyPicker.swift */,
-				95561E3E26AF25EF00CCB543 /* SubscriptionFeature.swift */,
-				95909CB226B07BFC00D051AB /* SearchBar.swift */,
-				9522BEAA26B5AACB0076B098 /* ListModifier.swift */,
 			);
 			path = Helpers;
 			sourceTree = "<group>";
@@ -451,6 +471,24 @@
 			productReference = 950093C726CBC7A200FEBF67 /* SimoleonAppPreview.xctest */;
 			productType = "com.apple.product-type.bundle.ui-testing";
 		};
+		9531D43E26D8E4CF00665D2A /* SimoleonTests */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 9531D44626D8E4CF00665D2A /* Build configuration list for PBXNativeTarget "SimoleonTests" */;
+			buildPhases = (
+				9531D43B26D8E4CF00665D2A /* Sources */,
+				9531D43C26D8E4CF00665D2A /* Frameworks */,
+				9531D43D26D8E4CF00665D2A /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				9531D44526D8E4CF00665D2A /* PBXTargetDependency */,
+			);
+			name = SimoleonTests;
+			productName = SimoleonTests;
+			productReference = 9531D43F26D8E4CF00665D2A /* SimoleonTests.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
 		959F6DE726BBD53500101E53 /* SimoleonScreenshots */ = {
 			isa = PBXNativeTarget;
 			buildConfigurationList = 959F6DF126BBD53500101E53 /* Build configuration list for PBXNativeTarget "SimoleonScreenshots" */;
@@ -490,26 +528,6 @@
 			productReference = 95C5B2242697752600941585 /* Simoleon.app */;
 			productType = "com.apple.product-type.application";
 		};
-		95C5B2392697752700941585 /* SimoleonTests */ = {
-			isa = PBXNativeTarget;
-			buildConfigurationList = 95C5B2512697752700941585 /* Build configuration list for PBXNativeTarget "SimoleonTests" */;
-			buildPhases = (
-				95C5B2362697752700941585 /* Sources */,
-				95C5B2372697752700941585 /* Frameworks */,
-				95C5B2382697752700941585 /* Resources */,
-			);
-			buildRules = (
-			);
-			dependencies = (
-				95C5B23C2697752700941585 /* PBXTargetDependency */,
-			);
-			name = SimoleonTests;
-			packageProductDependencies = (
-			);
-			productName = SimoleonTests;
-			productReference = 95C5B23A2697752700941585 /* SimoleonTests.xctest */;
-			productType = "com.apple.product-type.bundle.unit-test";
-		};
 		95C5B2442697752700941585 /* SimoleonUITests */ = {
 			isa = PBXNativeTarget;
 			buildConfigurationList = 95C5B2542697752700941585 /* Build configuration list for PBXNativeTarget "SimoleonUITests" */;
@@ -541,6 +559,10 @@
 						CreatedOnToolsVersion = 12.5.1;
 						TestTargetID = 95C5B2232697752600941585;
 					};
+					9531D43E26D8E4CF00665D2A = {
+						CreatedOnToolsVersion = 12.5.1;
+						TestTargetID = 95C5B2232697752600941585;
+					};
 					959F6DE726BBD53500101E53 = {
 						CreatedOnToolsVersion = 12.5.1;
 						TestTargetID = 95C5B2232697752600941585;
@@ -548,10 +570,6 @@
 					95C5B2232697752600941585 = {
 						CreatedOnToolsVersion = 12.5.1;
 					};
-					95C5B2392697752700941585 = {
-						CreatedOnToolsVersion = 12.5.1;
-						TestTargetID = 95C5B2232697752600941585;
-					};
 					95C5B2442697752700941585 = {
 						CreatedOnToolsVersion = 12.5.1;
 						TestTargetID = 95C5B2232697752600941585;
@@ -583,7 +601,7 @@
 			projectRoot = "";
 			targets = (
 				95C5B2232697752600941585 /* Simoleon */,
-				95C5B2392697752700941585 /* SimoleonTests */,
+				9531D43E26D8E4CF00665D2A /* SimoleonTests */,
 				95C5B2442697752700941585 /* SimoleonUITests */,
 				959F6DE726BBD53500101E53 /* SimoleonScreenshots */,
 				950093C626CBC7A200FEBF67 /* SimoleonAppPreview */,
@@ -599,6 +617,13 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		9531D43D26D8E4CF00665D2A /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		959F6DE626BBD53500101E53 /* Resources */ = {
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
@@ -616,19 +641,12 @@
 				9501E13A26D3A55200D14DE5 /* CurrencyPairsSupported.json in Resources */,
 				95B5F53326AADE4B00BDCE89 /* Localizable.strings in Resources */,
 				95B5F53226AADE4B00BDCE89 /* contents.json in Resources */,
-				9501E13826D3A52000D14DE5 /* CurrencyDetails.json in Resources */,
+				9501E13826D3A52000D14DE5 /* Currencies.json in Resources */,
 				95B5F53426AADE4B00BDCE89 /* InfoPlist.strings in Resources */,
 				95C5B22C2697752700941585 /* Assets.xcassets in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
-		95C5B2382697752700941585 /* Resources */ = {
-			isa = PBXResourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
 		95C5B2432697752700941585 /* Resources */ = {
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
@@ -647,12 +665,20 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		9531D43B26D8E4CF00665D2A /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				9531D44226D8E4CF00665D2A /* SimoleonTests.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		959F6DE426BBD53500101E53 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 				959F6DEB26BBD53500101E53 /* SimoleonScreenshots.swift in Sources */,
-				959F6DF326BBD54400101E53 /* SnapshotHelper.swift in Sources */,
+				95DA4B5626D7D10100566C5E /* SnapshotHelper.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -661,46 +687,42 @@
 			buildActionMask = 2147483647;
 			files = (
 				95C5179926A5EC9F00BC2B24 /* FavoriteButton.swift in Sources */,
-				95C5179C26A5EFBE00BC2B24 /* Favorite+CoreDataClass.swift in Sources */,
-				950A377826A820F800CAB175 /* DefaultCurrency+CoreDataClass.swift in Sources */,
+				95851CF126D4E89C004ADA79 /* Favorite+CoreDataProperties.swift in Sources */,
 				9522CD9D26CED2E100DD9D03 /* ErrorHandling.swift in Sources */,
+				95AA42ED26D78A4A0085570D /* FileHelper.swift in Sources */,
+				95851CE326D4DAAE004ADA79 /* CurrencyButton.swift in Sources */,
 				95C5B2312697752700941585 /* Persistence.swift in Sources */,
-				9585BB1226A6B71B00E3193E /* ReadConfig.swift in Sources */,
+				95DA4B5926D7E2DE00566C5E /* ChildListResets.swift in Sources */,
+				95851CF026D4E89C004ADA79 /* Favorite+CoreDataClass.swift in Sources */,
 				95AEBC9526A03ECB00613729 /* ContentView.swift in Sources */,
 				9522BEAB26B5AACB0076B098 /* ListModifier.swift in Sources */,
 				95909CB326B07BFC00D051AB /* SearchBar.swift in Sources */,
+				95C1DD6A26D8DF9400315C3F /* CurrencySelector.swift in Sources */,
 				9522BEA926B5A4D20076B098 /* AppDelegate.swift in Sources */,
+				95851CE526D4DB4C004ADA79 /* Flag.swift in Sources */,
 				95D8C8CD26A9784500BCC188 /* SubscribeButton.swift in Sources */,
-				950A377726A820F800CAB175 /* DefaultCurrency+CoreDataProperties.swift in Sources */,
-				9585BB1A26A6E8FD00E3193E /* SimpleSuccess.swift in Sources */,
-				9555933A269B0AB8000FD726 /* Read.swift in Sources */,
+				95851CE926D4E552004ADA79 /* DefaultCurrency+CoreDataProperties.swift in Sources */,
+				9585BB1A26A6E8FD00E3193E /* HapticsHelper.swift in Sources */,
 				95D8C8CF26A98A7900BCC188 /* RestoreButton.swift in Sources */,
-				95C5179D26A5EFBE00BC2B24 /* Favorite+CoreDataProperties.swift in Sources */,
-				95C5179F26A5F34200BC2B24 /* Favorites.swift in Sources */,
+				95C5179F26A5F34200BC2B24 /* FavoritesView.swift in Sources */,
 				95C5B2282697752600941585 /* SimoleonApp.swift in Sources */,
 				95B54F4A26A4A450001DC0D8 /* ConversionBox.swift in Sources */,
 				95D8C8C726A95D2900BCC188 /* SubscriptionPaywall.swift in Sources */,
 				95D8C8D126A9BC6200BCC188 /* LockedCurrencyPicker.swift in Sources */,
 				95C517A126A5F6C000BC2B24 /* ResignKeyboard.swift in Sources */,
+				95CE6A3626D50B7700D9DCBD /* CurrencyList.swift in Sources */,
+				957DCF3326D7ADEA00BCAB1E /* CurrencyPairModel.swift in Sources */,
 				95AEBC9D26A04D4600613729 /* CurrencyRow.swift in Sources */,
 				95AEBCA326A0900E00613729 /* CurrencyQuoteModel.swift in Sources */,
-				9585BB1426A6B7F400E3193E /* NetworkRequest.swift in Sources */,
-				957065E226A5FE0400523E68 /* Settings.swift in Sources */,
-				95B54F4426A4842C001DC0D8 /* Conversion.swift in Sources */,
+				9585BB1426A6B7F400E3193E /* NetworkHelper.swift in Sources */,
+				957065E226A5FE0400523E68 /* SettingsView.swift in Sources */,
+				95B54F4426A4842C001DC0D8 /* ConversionView.swift in Sources */,
 				95C5B2342697752700941585 /* Simoleon.xcdatamodeld in Sources */,
 				953B8B1726D3A970003CF530 /* CurrencyDetailsModel.swift in Sources */,
 				95C5179126A5DC8E00BC2B24 /* ConditionalWrapper.swift in Sources */,
 				95B54F5126A4ACAC001DC0D8 /* Sidebar.swift in Sources */,
-				95B54F4626A48852001DC0D8 /* CurrencySelector.swift in Sources */,
 				95561E3F26AF25EF00CCB543 /* SubscriptionFeature.swift in Sources */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-		95C5B2362697752700941585 /* Sources */ = {
-			isa = PBXSourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				95C5B23F2697752700941585 /* SimoleonTests.swift in Sources */,
+				95851CE826D4E552004ADA79 /* DefaultCurrency+CoreDataClass.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -720,16 +742,16 @@
 			target = 95C5B2232697752600941585 /* Simoleon */;
 			targetProxy = 950093CC26CBC7A200FEBF67 /* PBXContainerItemProxy */;
 		};
+		9531D44526D8E4CF00665D2A /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 95C5B2232697752600941585 /* Simoleon */;
+			targetProxy = 9531D44426D8E4CF00665D2A /* PBXContainerItemProxy */;
+		};
 		959F6DEE26BBD53500101E53 /* PBXTargetDependency */ = {
 			isa = PBXTargetDependency;
 			target = 95C5B2232697752600941585 /* Simoleon */;
 			targetProxy = 959F6DED26BBD53500101E53 /* PBXContainerItemProxy */;
 		};
-		95C5B23C2697752700941585 /* PBXTargetDependency */ = {
-			isa = PBXTargetDependency;
-			target = 95C5B2232697752600941585 /* Simoleon */;
-			targetProxy = 95C5B23B2697752700941585 /* PBXContainerItemProxy */;
-		};
 		95C5B2472697752700941585 /* PBXTargetDependency */ = {
 			isa = PBXTargetDependency;
 			target = 95C5B2232697752600941585 /* Simoleon */;
@@ -833,6 +855,69 @@
 			};
 			name = Release;
 		};
+		9531D44726D8E4CF00665D2A /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_TEAM = MTX83R5H8X;
+				INFOPLIST_FILE = SimoleonTests/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 14.5;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = io.dennistech.SimoleonTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Simoleon.app/Simoleon";
+			};
+			name = Debug;
+		};
+		9531D44826D8E4CF00665D2A /* Screenshots */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_TEAM = MTX83R5H8X;
+				INFOPLIST_FILE = SimoleonTests/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 14.5;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = io.dennistech.SimoleonTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Simoleon.app/Simoleon";
+			};
+			name = Screenshots;
+		};
+		9531D44926D8E4CF00665D2A /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_TEAM = MTX83R5H8X;
+				INFOPLIST_FILE = SimoleonTests/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 14.5;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = io.dennistech.SimoleonTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Simoleon.app/Simoleon";
+			};
+			name = Release;
+		};
 		959F6DEF26BBD53500101E53 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
@@ -966,28 +1051,6 @@
 			};
 			name = Screenshots;
 		};
-		95B9EECF26CAC9EF00D94D60 /* Screenshots */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
-				BUNDLE_LOADER = "$(TEST_HOST)";
-				CODE_SIGN_STYLE = Automatic;
-				DEVELOPMENT_TEAM = MTX83R5H8X;
-				INFOPLIST_FILE = SimoleonTests/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@loader_path/Frameworks",
-				);
-				PRODUCT_BUNDLE_IDENTIFIER = io.dennistech.SimoleonTests;
-				PRODUCT_NAME = "$(TARGET_NAME)";
-				SWIFT_VERSION = 5.0;
-				TARGETED_DEVICE_FAMILY = "1,2";
-				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Simoleon.app/Simoleon";
-			};
-			name = Screenshots;
-		};
 		95B9EED026CAC9EF00D94D60 /* Screenshots */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
@@ -1200,50 +1263,6 @@
 			};
 			name = Release;
 		};
-		95C5B2522697752700941585 /* Debug */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
-				BUNDLE_LOADER = "$(TEST_HOST)";
-				CODE_SIGN_STYLE = Automatic;
-				DEVELOPMENT_TEAM = MTX83R5H8X;
-				INFOPLIST_FILE = SimoleonTests/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@loader_path/Frameworks",
-				);
-				PRODUCT_BUNDLE_IDENTIFIER = io.dennistech.SimoleonTests;
-				PRODUCT_NAME = "$(TARGET_NAME)";
-				SWIFT_VERSION = 5.0;
-				TARGETED_DEVICE_FAMILY = "1,2";
-				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Simoleon.app/Simoleon";
-			};
-			name = Debug;
-		};
-		95C5B2532697752700941585 /* Release */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
-				BUNDLE_LOADER = "$(TEST_HOST)";
-				CODE_SIGN_STYLE = Automatic;
-				DEVELOPMENT_TEAM = MTX83R5H8X;
-				INFOPLIST_FILE = SimoleonTests/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@loader_path/Frameworks",
-				);
-				PRODUCT_BUNDLE_IDENTIFIER = io.dennistech.SimoleonTests;
-				PRODUCT_NAME = "$(TARGET_NAME)";
-				SWIFT_VERSION = 5.0;
-				TARGETED_DEVICE_FAMILY = "1,2";
-				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Simoleon.app/Simoleon";
-			};
-			name = Release;
-		};
 		95C5B2552697752700941585 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
@@ -1297,6 +1316,16 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
+		9531D44626D8E4CF00665D2A /* Build configuration list for PBXNativeTarget "SimoleonTests" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				9531D44726D8E4CF00665D2A /* Debug */,
+				9531D44826D8E4CF00665D2A /* Screenshots */,
+				9531D44926D8E4CF00665D2A /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 		959F6DF126BBD53500101E53 /* Build configuration list for PBXNativeTarget "SimoleonScreenshots" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
@@ -1327,16 +1356,6 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
-		95C5B2512697752700941585 /* Build configuration list for PBXNativeTarget "SimoleonTests" */ = {
-			isa = XCConfigurationList;
-			buildConfigurations = (
-				95C5B2522697752700941585 /* Debug */,
-				95B9EECF26CAC9EF00D94D60 /* Screenshots */,
-				95C5B2532697752700941585 /* Release */,
-			);
-			defaultConfigurationIsVisible = 0;
-			defaultConfigurationName = Release;
-		};
 		95C5B2542697752700941585 /* Build configuration list for PBXNativeTarget "SimoleonUITests" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
--- a/Simoleon.xcodeproj/xcshareddata/xcschemes/Simoleon.xcscheme	Wed Aug 25 11:00:21 2021 +0100
+++ b/Simoleon.xcodeproj/xcshareddata/xcschemes/Simoleon.xcscheme	Sat Aug 28 11:15:25 2021 +0100
@@ -42,7 +42,7 @@
             buildForAnalyzing = "YES">
             <BuildableReference
                BuildableIdentifier = "primary"
-               BlueprintIdentifier = "95C5B2392697752700941585"
+               BlueprintIdentifier = "9531D43E26D8E4CF00665D2A"
                BuildableName = "SimoleonTests.xctest"
                BlueprintName = "SimoleonTests"
                ReferencedContainer = "container:Simoleon.xcodeproj">
@@ -69,13 +69,14 @@
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       shouldUseLaunchSchemeArgsEnv = "YES"
-      language = "en">
+      language = "en"
+      codeCoverageEnabled = "YES">
       <Testables>
          <TestableReference
             skipped = "NO">
             <BuildableReference
                BuildableIdentifier = "primary"
-               BlueprintIdentifier = "95C5B2392697752700941585"
+               BlueprintIdentifier = "9531D43E26D8E4CF00665D2A"
                BuildableName = "SimoleonTests.xctest"
                BlueprintName = "SimoleonTests"
                ReferencedContainer = "container:Simoleon.xcodeproj">
--- a/Simoleon/ContentView.swift	Wed Aug 25 11:00:21 2021 +0100
+++ b/Simoleon/ContentView.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -10,7 +10,6 @@
 struct ContentView: View {
     @Environment(\.managedObjectContext) private var viewContext
     @FetchRequest(sortDescriptors: []) private var defaultCurrency: FetchedResults<DefaultCurrency>
-    
     @State private var tab: Tab = .convert
     
     private enum Tab {
@@ -18,32 +17,34 @@
     }
     
     @ViewBuilder var adjustedView: some View {
+        let currencyPair = CurrencyPairModel(baseSymbol: "USD", quoteSymbol: "EUR")
+        
         // MARK: - iPad
         if UIDevice.current.userInterfaceIdiom == .pad {
             NavigationView {
-                Sidebar()
-                Conversion(currencyPair: defaultCurrency.first?.pair ?? "USD/GBP")
+//                Sidebar()
+                ConversionView(currencyPair: currencyPair)
             }
         } else {
             // MARK: - iPhone
             TabView(selection: $tab) {
-                Conversion(currencyPair: defaultCurrency.first?.pair ?? "USD/GBP")
+                ConversionView(currencyPair: currencyPair)
                     .tabItem {
                         Label("Convert", systemImage: "arrow.counterclockwise.circle")
                     }
                     .tag(Tab.convert)
                 
-                Favorites()
-                    .tabItem {
-                        Label("Favorites", systemImage: "star")
-                    }
-                    .tag(Tab.favorites)
-                
-                Settings()
-                    .tabItem {
-                        Label("Settings", systemImage: "gear")
-                    }
-                    .tag(Tab.settings)
+//                FavoritesView()
+//                    .tabItem {
+//                        Label("Favorites", systemImage: "star")
+//                    }
+//                    .tag(Tab.favorites)
+//
+//                SettingsView()
+//                    .tabItem {
+//                        Label("Settings", systemImage: "gear")
+//                    }
+//                    .tag(Tab.settings)
             }
         }
     }
--- a/Simoleon/Conversion.swift	Wed Aug 25 11:00:21 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +0,0 @@
-//
-//  Conversion.swift
-//  Simoleon
-//
-//  Created by Dennis Concepción Martín on 18/07/2021.
-//
-
-import SwiftUI
-import Purchases
-
-struct Conversion: View {
-    var showNavigationView: Bool?
-    
-    @State var currencyPair: String
-    @State private var amountToConvert = ""
-    @State private var price: Double = 1.00
-    @State private var showingConversion = false
-    @State private var showingCurrencySelector = false
-    @State private var amountIsEditing = false
-    
-    var body: some View {
-        ScrollView(showsIndicators: false) {
-            VStack(alignment: .leading) {
-                HStack {
-                    Button(action: { showingCurrencySelector = true }) {
-                        RoundedRectangle(cornerRadius: 15)
-                            .foregroundColor(Color(.secondarySystemBackground))
-                            .frame(height: 60)
-                            .overlay(
-                                CurrencyRow(currencyPairName: currencyPair)
-                                    .padding(.horizontal)
-                            )
-                    }
-                    .accessibilityIdentifier("OpenCurrencySelector")
-                    
-                    FavoriteButton(currencyPair: currencyPair)
-                }
-                
-                ConversionBox(
-                    currencyPair: $currencyPair,
-                    amountToConvert: $amountToConvert,
-                    price: $price,
-                    showingConversion: $showingConversion,
-                    amountIsEditing: $amountIsEditing
-                )
-            }
-            .padding()
-            .sheet(isPresented: $showingCurrencySelector, onDismiss: request) {
-                CurrencySelector(currencyPair: $currencyPair, showingCurrencySelector: $showingCurrencySelector)
-            }
-        }
-        .onAppear(perform: request)
-        .navigationTitle("Convert")
-        .toolbar {
-            ToolbarItem(placement: .navigationBarTrailing) {
-                if amountIsEditing {
-                    Button(action: {
-                        UIApplication.shared.dismissKeyboard()
-                        amountIsEditing = false
-                    }) {
-                        Text("Done")
-                    }
-                }
-            }
-        }
-        .if(UIDevice.current.userInterfaceIdiom == .phone && showNavigationView ?? true) { content in
-            NavigationView { content }
-        }
-    }
-    
-    private func request() {
-        showingConversion = false
-        let url = "\(readConfig("API_URL")!)quotes?pairs=\(currencyPair)&api_key=\(readConfig("API_KEY")!)"
-        networkRequest(url: url, model: [CurrencyQuoteModel].self) { response in
-            if let price = response.first?.price {
-                self.price = price
-                showingConversion =  true
-            } else {
-                // Handle error
-            }
-        }
-    }
-}
-
-
-struct Conversion_Previews: PreviewProvider {
-    static var previews: some View {
-        Conversion(currencyPair: "USD/GBP")
-    }
-}
--- a/Simoleon/ErrorHandling.swift	Wed Aug 25 11:00:21 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-//
-//  ErrorHandling.swift
-//  Simoleon
-//
-//  Created by Dennis Concepción Martín on 19/8/21.
-//
-
-import Foundation
-
-class ErrorHandling {
-    enum Json: Error {
-        case fileMissing
-        case loadFailed(cause: String)
-        case parseFailed(cause: String)
-    }
-}
--- a/Simoleon/Favorites.swift	Wed Aug 25 11:00:21 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-//
-//  Favorites.swift
-//  Simoleon
-//
-//  Created by Dennis Concepción Martín on 19/07/2021.
-//
-
-import SwiftUI
-
-struct Favorites: View {
-    @Environment(\.managedObjectContext) private var viewContext
-    @FetchRequest(
-        sortDescriptors: [NSSortDescriptor(keyPath: \Favorite.currencyPair, ascending: true)],
-        animation: .default) private var favorites: FetchedResults<Favorite>
-    
-    var body: some View {
-        VStack {
-            if favorites.isEmpty {
-                Group {
-                    Image(systemName: "star")
-                        .font(.title)
-                    
-                    Text("Search a currency pair and add it to favorites.")
-                        .padding(.top, 5)
-                }
-                .multilineTextAlignment(.center)
-                .foregroundColor(.secondary)
-                .padding(.horizontal, 50)
-            } else {
-                List {
-                    ForEach(favorites) { favorite in
-                        NavigationLink(destination: Conversion(showNavigationView: false, currencyPair: favorite.currencyPair)) {
-                            CurrencyRow(currencyPairName: favorite.currencyPair)
-                        }
-                    }
-                    .onDelete(perform: removeFromFavorites)
-                }
-                .listStyle(PlainListStyle())
-                .accessibilityIdentifier("FavoritesList")
-            }
-        }
-        .navigationTitle("Favorites")
-        .toolbar {
-            #if os(iOS)
-            EditButton()
-            #endif
-        }
-        .if(UIDevice.current.userInterfaceIdiom == .phone) { content in
-            NavigationView { content }
-        }
-        .onAppear {
-            #if SCREENSHOTS
-            generateFavoritesForScreenshots()
-            #endif
-        }
-    }
-    
-    private func removeFromFavorites(offsets: IndexSet) {
-        withAnimation {
-            offsets.map { favorites[$0] }.forEach(viewContext.delete)
-            
-            do {
-                try viewContext.save()
-            } catch {
-                let nsError = error as NSError
-                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
-            }
-        }
-    }
-
-    #if SCREENSHOTS
-    /*
-     Save currencies to favourites to take screenshots for the App Store
-     */
-    private func generateFavoritesForScreenshots() {
-        let favoriteCurrencies = [
-            "EUR/USD", "BTC/USD", "USD/HKD", "USD/JPY", "AUD/USD",
-            "XAU/GBP", "DASH/ETH", "EUR/USD", "XAG/CAD"
-        ]
-        
-        let coreDataCurrencyPairs = favorites.map { $0.currencyPair }
-        
-        for favoriteCurrency in favoriteCurrencies {
-            if !coreDataCurrencyPairs.contains(favoriteCurrency) {
-                let favorites = Favorite(context: viewContext)
-                favorites.currencyPair = favoriteCurrency
-                
-                do {
-                    try viewContext.save()
-                } catch {
-                    let nsError = error as NSError
-                    fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
-                }
-            }
-        }
-    }
-    #endif
-}
-
-struct Favorites_Previews: PreviewProvider {
-    static var previews: some View {
-        Favorites()
-            .environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/FavoritesView.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -0,0 +1,105 @@
+////
+////  FavoritesView.swift
+////  Simoleon
+////
+////  Created by Dennis Concepción Martín on 19/07/2021.
+////
+//
+//import SwiftUI
+//
+//struct FavoritesView: View {
+//    @Environment(\.managedObjectContext) private var viewContext
+//    @FetchRequest(
+//        sortDescriptors: [NSSortDescriptor(keyPath: \Favorite.currencyPair, ascending: true)],
+//        animation: .default) private var favorites: FetchedResults<Favorite>
+//    
+//    var body: some View {
+//        VStack {
+//            if favorites.isEmpty {
+//                Group {
+//                    Image(systemName: "star")
+//                        .font(.title)
+//                    
+//                    Text("Search a currency pair and add it to favorites.")
+//                        .padding(.top, 5)
+//                }
+//                .multilineTextAlignment(.center)
+//                .foregroundColor(.secondary)
+//                .padding(.horizontal, 50)
+//            } else {
+//                List {
+//                    ForEach(favorites) { favorite in
+//                        NavigationLink(destination: Conversion()) {
+////                            CurrencyRow(currencyPairName: favorite.currencyPair)
+//                        }
+//                    }
+//                    .onDelete(perform: removeFromFavorites)
+//                }
+//                .listStyle(PlainListStyle())
+//                .accessibilityIdentifier("FavoritesList")
+//            }
+//        }
+//        .navigationTitle("Favorites")
+//        .toolbar {
+//            #if os(iOS)
+//            EditButton()
+//            #endif
+//        }
+//        .if(UIDevice.current.userInterfaceIdiom == .phone) { content in
+//            NavigationView { content }
+//        }
+//        .onAppear {
+//            #if SCREENSHOTS
+//            generateFavoritesForScreenshots()
+//            #endif
+//        }
+//    }
+//    
+//    private func removeFromFavorites(offsets: IndexSet) {
+//        withAnimation {
+//            offsets.map { favorites[$0] }.forEach(viewContext.delete)
+//            
+//            do {
+//                try viewContext.save()
+//            } catch {
+//                let nsError = error as NSError
+//                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
+//            }
+//        }
+//    }
+//
+//    #if SCREENSHOTS
+//    /*
+//     Save currencies to favourites to take screenshots for the App Store
+//     */
+//    private func generateFavoritesForScreenshots() {
+//        let favoriteCurrencies = [
+//            "EUR/USD", "BTC/USD", "USD/HKD", "USD/JPY", "AUD/USD",
+//            "XAU/GBP", "DASH/ETH", "EUR/USD", "XAG/CAD"
+//        ]
+//        
+//        let coreDataCurrencyPairs = favorites.map { $0.currencyPair }
+//        
+//        for favoriteCurrency in favoriteCurrencies {
+//            if !coreDataCurrencyPairs.contains(favoriteCurrency) {
+//                let favorites = Favorite(context: viewContext)
+//                favorites.currencyPair = favoriteCurrency
+//                
+//                do {
+//                    try viewContext.save()
+//                } catch {
+//                    let nsError = error as NSError
+//                    fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
+//                }
+//            }
+//        }
+//    }
+//    #endif
+//}
+//
+//struct FavoritesView_Previews: PreviewProvider {
+//    static var previews: some View {
+//        FavoritesView()
+//            .environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
+//    }
+//}
--- a/Simoleon/Helpers/ConversionBox.swift	Wed Aug 25 11:00:21 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +0,0 @@
-//
-//  ConversionBox.swift
-//  Simoleon
-//
-//  Created by Dennis Concepción Martín on 18/07/2021.
-//
-
-import SwiftUI
-
-struct ConversionBox: View {
-    @Binding var currencyPair: String
-    @Binding var amountToConvert: String
-    @Binding var price: Double
-    @Binding var showingConversion: Bool
-    @Binding var amountIsEditing: Bool
-    
-    var body: some View {
-        VStack(alignment: .leading) {
-            let currencyDetails: [String: CurrencyDetailsModel] = try! read(json: "CurrencyDetails.json")
-            let currencies = currencyPair.split(separator: "/")
-            Text("\(currencyDetails[String(currencies[0])]!.name) (\(String(currencies[0])))")
-                .font(.callout)
-                .fontWeight(.semibold)
-                .padding(.top, 40)
-            
-            ZStack(alignment: .trailing) {
-                TextField("Enter amount", text: $amountToConvert) { startedEditing in
-                    if startedEditing {
-                        withAnimation {
-                            amountIsEditing = true
-                        }
-                    }
-                }
-                onCommit: {
-                    withAnimation {
-                        amountIsEditing = false
-                    }
-                }
-                .keyboardType(.decimalPad)
-                .font(Font.title.weight(.semibold))
-                .lineLimit(1)
-                .accessibilityIdentifier("ConversionTextField")
-            }
-            
-            Divider()
-            
-            Text("\(currencyDetails[String(currencies[1])]!.name) (\(String(currencies[1])))")
-                .font(.callout)
-                .fontWeight(.semibold)
-                .padding(.top, 10)
-            
-            if showingConversion {
-                Text("\(makeConversion(), specifier: "%.2f")")
-                    .font(Font.title.weight(.semibold))
-                    .lineLimit(1)
-                    .padding(.top, 5)
-            } else {
-                ProgressView()
-                    .padding(.top, 5)
-            }
-        }
-    }
-    
-    /*
-     if the amount can be converted to Double:
-     * Return amount
-     else:
-     * Return zero
-     */
-    func makeConversion() -> Double {
-        if let amountToConvert = Double(amountToConvert) {
-            return amountToConvert * price  // Conversion
-        } else {
-            return 0
-        }
-    }
-}
-
-
-struct ConversionBox_Previews: PreviewProvider {
-    static var previews: some View {
-        ConversionBox(
-            currencyPair: .constant("USD/GBP"),
-            amountToConvert: .constant("1000"),
-            price: .constant(1),
-            showingConversion: .constant(false),
-            amountIsEditing: .constant(false)
-        )
-    }
-}
--- a/Simoleon/Helpers/CurrencyRow.swift	Wed Aug 25 11:00:21 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-//
-//  CurrencyRow.swift
-//  Simoleon
-//
-//  Created by Dennis Concepción Martín on 15/07/2021.
-//
-
-import SwiftUI
-
-struct CurrencyRow: View {
-    var currencyPairName: String
-    var isLocked: Bool?
-    
-    var body: some View {
-        HStack {
-            let currencyDetails: [String: CurrencyDetailsModel] = try! read(json: "CurrencyDetails.json")
-            let currencies = currencyPairName.split(separator: "/")
-            Image(currencyDetails[String(currencies[0])]!.flag)
-                .resizable()
-                .aspectRatio(contentMode: .fill)
-                .frame(width: 30, height: 30)
-                .clipShape(Circle())
-                .overlay(Circle().stroke(Color(.secondaryLabel), lineWidth: 1))
-            
-            Image(currencyDetails[String(currencies[1])]!.flag)
-                .resizable()
-                .aspectRatio(contentMode: .fill)
-                .frame(width: 30, height: 30)
-                .clipShape(Circle())
-                .overlay(Circle().stroke(Color(.secondaryLabel), lineWidth: 1))
-                .offset(x: -20)
-                .padding(.trailing, -20)
-            
-            Text("From \(String(currencies[0])) to \(String(currencies[1]))")
-                .fontWeight(.semibold)
-                .foregroundColor(Color("PlainButton"))
-                .padding(.leading)
-            
-            Spacer()
-            
-            if isLocked ?? false {
-                Image(systemName: "lock")
-                    .foregroundColor(.secondary)
-            }
-        }
-    }
-}
-
-struct CurrencyRow_Previews: PreviewProvider {
-    static var previews: some View {
-        CurrencyRow(currencyPairName: "USD/GBP", isLocked: true)
-    }
-}
--- a/Simoleon/Helpers/CurrencySelector.swift	Wed Aug 25 11:00:21 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +0,0 @@
-//
-//  CurrencySelector.swift
-//  Simoleon
-//
-//  Created by Dennis Concepción Martín on 18/07/2021.
-//
-
-import SwiftUI
-import Purchases
-
-struct CurrencySelector: View {
-    @Binding var currencyPair: String
-    @Binding var showingCurrencySelector: Bool
-    
-    @State private var entitlementIsActive = false
-    @State private var searchCurrency = ""
-    @State private var showingSubscriptionPaywall = false
-    @State private var alertTitle = ""
-    @State private var alertMessage = ""
-    @State private var showingAlert = false
-    
-    /*
-     If searched currency string is empty:
-     * Show all currencies
-     else:
-     * Show filtered list of currencies containing searched currency string
-     */
-    var searchResults: [String] {
-        let currencyPairsSupported: [String] = try! read(json: "CurrencyPairs.json")
-        if searchCurrency.isEmpty {
-            return currencyPairsSupported.sorted()
-        } else {
-            return currencyPairsSupported.filter { $0.contains(searchCurrency.uppercased()) }
-        }
-    }
-    
-    var body: some View {
-        NavigationView {
-            VStack {
-                SearchBar(placeholder: "Search...", text: $searchCurrency)
-                    .padding()
-                    .accessibilityIdentifier("CurrencySearchBar")
-                
-                List {
-                    if entitlementIsActive {
-                        ForEach(searchResults, id: \.self) { currencyPairsSupported in
-                            Button(action: {
-                                self.currencyPair = currencyPairsSupported
-                                showingCurrencySelector = false
-                            }) {
-                                CurrencyRow(currencyPairName: currencyPairsSupported)
-                            }
-                        }
-                    } else {
-                        ForEach(searchResults, id: \.self) { currencyPairsSupported in
-                            Button(action: { select(currencyPairsSupported) }) {
-                                CurrencyRow(currencyPairName: currencyPairsSupported, isLocked: false)
-                            }
-                        }
-                    }
-                }
-                .id(UUID())
-            }
-            .navigationTitle("Currencies")
-            .navigationBarTitleDisplayMode(.inline)
-            .toolbar {
-                ToolbarItem(placement: .cancellationAction) {
-                    Button(action: { showingCurrencySelector = false }) {
-                        Text("Cancel")
-                    }
-                }
-            }
-        }
-        .onAppear(perform: checkEntitlement)
-        .alert(isPresented: $showingAlert) {
-            Alert(title: Text(alertTitle), message: Text(alertMessage), dismissButton: .default(Text("Ok")))
-        }
-        .sheet(isPresented: $showingSubscriptionPaywall, onDismiss: checkEntitlement) {
-            SubscriptionPaywall(showingSubscriptionPaywall: $showingSubscriptionPaywall)
-        }
-    }
-    
-    /*
-     If user is subscribed:
-     * Select currency and dismiss currency selector
-     else:
-     * Show subscription paywall
-     */
-    private func select(_ currencyPair: String) {
-//        if currencyPair.isLocked {
-//            showingSubscriptionPaywall = true
-//        } else {
-//            self.currencyPair = currencyPair.name
-//            showingCurrencySelector = false
-//        }
-    }
-    
-    // Check if user subscription is active
-    private func checkEntitlement() {
-        #if SCREENSHOTS
-        entitlementIsActive = true
-        #else
-        Purchases.shared.purchaserInfo { (purchaserInfo, error) in
-            if purchaserInfo?.entitlements["all"]?.isActive == true {
-                entitlementIsActive = true
-            }
-            
-            if let error = error as NSError? {
-                alertTitle = error.localizedDescription
-                alertMessage = error.localizedFailureReason ?? ""
-                showingAlert = true
-            }
-        }
-        #endif
-    }
-}
-extension View {
-    func listStyle() -> some View {
-        self.modifier(ListModifier())
-    }
-}
-
-
-struct CurrencySelector_Previews: PreviewProvider {
-    static var previews: some View {
-        CurrencySelector(
-            currencyPair: .constant("USD/GBP"),
-            showingCurrencySelector: .constant(false)
-        )
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/Helpers/ErrorHandling.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -0,0 +1,16 @@
+//
+//  ErrorHandling.swift
+//  Simoleon
+//
+//  Created by Dennis Concepción Martín on 19/8/21.
+//
+
+import Foundation
+
+class ErrorHandling {
+    enum Json: Error {
+        case fileMissing
+        case loadFailed(cause: String)
+        case parseFailed(cause: String)
+    }
+}
--- a/Simoleon/Helpers/FavoriteButton.swift	Wed Aug 25 11:00:21 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,104 +0,0 @@
-//
-//  FavoriteButton.swift
-//  Simoleon
-//
-//  Created by Dennis Concepción Martín on 19/07/2021.
-//
-
-import SwiftUI
-
-struct FavoriteButton: View {
-    var currencyPair: String
-    
-    @Environment(\.managedObjectContext) private var viewContext
-    @FetchRequest(sortDescriptors: []) private var favorites: FetchedResults<Favorite>
-    
-    @State private var starSymbol = "star"
-    
-    var body: some View {
-        let favoriteCurrencyPairs = favorites.map { $0.currencyPair }
-        Button(action: { favoriteAction(favoriteCurrencyPairs) }) {
-            RoundedRectangle(cornerRadius: 15)
-                .foregroundColor(Color(.secondarySystemBackground))
-                .frame(width: 60, height: 60)
-                .overlay(
-                    Image(systemName: generateStar(favoriteCurrencyPairs))
-                        .font(.system(size: 28))
-                        .foregroundColor(Color(.systemYellow))
-                )
-        }
-        .accessibilityIdentifier("AddToFavorites")
-    }
-    
-    /*
-     If currency pair is favorite:
-     * Button action is to remove from favorites
-     else:
-     * Button action is to add to favorites
-     */
-    private func favoriteAction(_ favoriteCurrencyPairs: [String]) {
-        if favoriteCurrencyPairs.contains(currencyPair) {
-            removeFromFavorites()
-        } else {
-            addToFavorites()
-        }
-        
-        simpleSuccess()
-    }
-    
-    /*
-     if currency pair is favorite:
-     * Return "star.fill" symbol
-     else:
-     * Return "star"
-     */
-    private func generateStar(_ favoriteCurrencyPairs: [String]) -> String {
-        if favoriteCurrencyPairs.contains(currencyPair) {
-            return "star.fill"
-        } else {
-            return "star"
-        }
-    }
-    
-    /*
-     * Get first favorite core data object that matches the specified currency pair
-     * Delete it
-     */
-    private func removeFromFavorites() {
-        withAnimation {
-            let favoriteObject = favorites.first(where: { $0.currencyPair == currencyPair })
-            viewContext.delete(favoriteObject ?? Favorite())
-            
-            do {
-                try viewContext.save()
-            } catch {
-                let nsError = error as NSError
-                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
-            }
-        }
-    }
-    
-    /*
-     * Create a favorite core data object
-     * Save it
-     */
-    private func addToFavorites() {
-        withAnimation {
-            let favorite = Favorite(context: viewContext)
-            favorite.currencyPair = currencyPair
-            
-            do {
-                try viewContext.save()
-            } catch {
-                let nsError = error as NSError
-                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
-            }
-        }
-    }
-}
-
-struct FavoriteButton_Previews: PreviewProvider {
-    static var previews: some View {
-        FavoriteButton(currencyPair: "USD/GBP")
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/Helpers/FileHelper.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -0,0 +1,41 @@
+//
+//  File.swift
+//  Simoleon
+//
+//  Created by Dennis Concepción Martín on 26/8/21.
+//
+
+import Foundation
+
+/*
+ Decode and read json file
+ */
+func read<T: Decodable>(json filename: String) throws -> T {
+    let data: Data
+    
+    guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
+    else {
+        throw ErrorHandling.Json.fileMissing
+    }
+    
+    do {
+        data = try Data(contentsOf: file)
+    } catch {
+        throw ErrorHandling.Json.loadFailed(cause: error.localizedDescription)
+    }
+    
+    do {
+        let decoder = JSONDecoder()
+        return try decoder.decode(T.self, from: data)
+    } catch {
+        throw ErrorHandling.Json.parseFailed(cause: error.localizedDescription)
+    }
+}
+
+/*
+ Read configuration variables from Config.xconfig
+ */
+func readConfigVariable(withKey: String) -> String? {
+    return (Bundle.main.infoDictionary?[withKey] as? String)?
+        .replacingOccurrences(of: "\\", with: "")
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/Helpers/HapticsHelper.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -0,0 +1,17 @@
+//
+//  SimpleSuccess.swift
+//  Simoleon
+//
+//  Created by Dennis Concepción Martín on 20/07/2021.
+//
+
+import SwiftUI
+
+class Haptics {
+    
+    // MARK: - Simple success
+    func simpleSuccess() {
+        let generator = UINotificationFeedbackGenerator()
+        generator.notificationOccurred(.success)
+    }
+}
--- a/Simoleon/Helpers/LockedCurrencyPicker.swift	Wed Aug 25 11:00:21 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-//
-//  LockedCurrencyPicker.swift
-//  Simoleon
-//
-//  Created by Dennis Concepción Martín on 22/07/2021.
-//
-
-import SwiftUI
-
-struct LockedCurrencyPicker: View {
-    @Environment(\.managedObjectContext) private var viewContext
-    @FetchRequest(sortDescriptors: []) private var defaultCurrency: FetchedResults<DefaultCurrency>
-    
-    var body: some View {
-        HStack {
-            Text("Default currency")
-            Spacer()
-            Text(defaultCurrency.first?.pair ?? "USD/GBP")
-                .foregroundColor(.secondary)
-            
-            Image(systemName: "lock")
-                .foregroundColor(.secondary)
-        }
-    }
-}
-
-struct LockedCurrencyPicker_Previews: PreviewProvider {
-    static var previews: some View {
-        LockedCurrencyPicker()
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/Helpers/NetworkHelper.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -0,0 +1,38 @@
+//
+//  Request.swift
+//  Simoleon
+//
+//  Created by Dennis Concepción Martín on 20/07/2021.
+//
+
+import Foundation
+
+// MARK: - HTTP Request
+func httpRequest<T: Decodable>(url: String, model: T.Type, completion: @escaping (_ result: T) -> Void) {
+    
+    // We take some model data T.Type
+    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/Helpers/Persistence.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -0,0 +1,64 @@
+//
+//  Persistence.swift
+//  Simoleon
+//
+//  Created by Dennis Concepción Martín on 08/07/2021.
+//
+
+import CoreData
+
+struct PersistenceController {
+    static let shared = PersistenceController()
+    
+    static var preview: PersistenceController = {
+        let result = PersistenceController(inMemory: true)
+        let viewContext = result.container.viewContext
+        
+        for _ in 0..<10 {
+            let favorite = Favorite(context: viewContext)
+            favorite.currencyPair = "GBP/USD"
+        }
+        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)")
+        }
+        return result
+    }()
+    
+    let container: NSPersistentCloudKitContainer
+    
+    init(inMemory: Bool = false) {
+        container = NSPersistentCloudKitContainer(name: "Simoleon")
+        container.viewContext.automaticallyMergesChangesFromParent = true
+        container.viewContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy
+        if inMemory {
+            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
+        }
+        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
+            if let error = error as NSError? {
+                /*
+                 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.
+                 */
+                
+                /*
+                 Typical reasons for an error here include:
+                 * The parent directory does not exist, cannot be created, or disallows writing.
+                 * The persistent store is not accessible, due to permissions or data protection when the device is locked.
+                 * The device is out of space.
+                 * The store could not be migrated to the current model version.
+                 Check the error message to determine what the actual problem was.
+                 */
+                fatalError("Unresolved error \(error), \(error.userInfo)")
+            }
+        })
+    }
+}
--- a/Simoleon/Helpers/RestoreButton.swift	Wed Aug 25 11:00:21 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-//
-//  RestoreButton.swift
-//  Simoleon
-//
-//  Created by Dennis Concepción Martín on 22/07/2021.
-//
-
-import SwiftUI
-import Purchases
-
-struct RestoreButton: View {
-    @Binding var showingSubscriptionPaywall: Bool
-    
-    @State private var alertTitle: LocalizedStringKey = ""
-    @State private var alertMessage: LocalizedStringKey = ""
-    @State private var restoringPurchases = false
-    @State private var showingAlert = false
-    
-    var body: some View {
-        Button(action: restorePurchases) {
-            if restoringPurchases {
-                ProgressView()
-            } else {
-                Text("Restore purchases")
-            }
-        }
-        .alert(isPresented: $showingAlert) {
-            Alert(title: Text(alertTitle), message: Text(alertMessage), dismissButton: .default(Text("Ok")))
-        }
-    }
-    
-    private func restorePurchases() {
-        restoringPurchases = true
-        
-        Purchases.shared.restoreTransactions { purchaserInfo, error in
-            if purchaserInfo?.entitlements["all"]?.isActive == true {
-                showingSubscriptionPaywall = false
-            } else {
-                alertTitle = LocalizedStringKey("No subscriptions found")
-                alertMessage = LocalizedStringKey("You are not subscripted to Simoleon yet.")
-                restoringPurchases = false
-                showingAlert = true
-            }
-            
-            if let error = error as NSError? {
-                alertTitle = LocalizedStringKey(error.localizedDescription)
-                alertMessage = LocalizedStringKey(error.localizedFailureReason ?? "")
-                showingAlert = true
-            }
-        }
-    }
-}
-
-struct RestoreButton_Previews: PreviewProvider {
-    static var previews: some View {
-        RestoreButton(showingSubscriptionPaywall: .constant(true))
-    }
-}
--- a/Simoleon/Helpers/SearchBar.swift	Wed Aug 25 11:00:21 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-//
-//  SearchBar.swift
-//  Simoleon
-//
-//  Created by Dennis Concepción Martín on 27/07/2021.
-//
-
-import SwiftUI
-
-struct SearchBar: View {
-    var placeholder: LocalizedStringKey
-    
-    @Binding var text: String
-    
-    var body: some View {
-        TextField(placeholder, text: $text)
-            .disableAutocorrection(true)
-            .padding(10)
-            .background(
-                RoundedRectangle(cornerRadius: 15)
-                    .foregroundColor(Color(.tertiarySystemFill))
-            )
-    }
-}
-
-struct SearchBar_Previews: PreviewProvider {
-    static var previews: some View {
-        SearchBar(placeholder: "Search ...", text: .constant(""))
-    }
-}
--- a/Simoleon/Helpers/Sidebar.swift	Wed Aug 25 11:00:21 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-//
-//  Sidebar.swift
-//  Simoleon
-//
-//  Created by Dennis Concepción Martín on 18/07/2021.
-//
-
-import SwiftUI
-
-struct Sidebar: View {
-    @Environment(\.managedObjectContext) private var viewContext
-    @FetchRequest(sortDescriptors: []) private var defaultCurrency: FetchedResults<DefaultCurrency>
-    
-    var body: some View {
-        List {
-            NavigationLink(destination: Conversion(currencyPair: defaultCurrency.first?.pair ?? "USD/GBP")) {
-                Label("Convert", systemImage: "arrow.counterclockwise.circle")
-            }
-            .accessibilityIdentifier("NavigateToConversion")
-            
-            NavigationLink(destination: Favorites()) {
-                Label("Favorites", systemImage: "star")
-            }
-            .accessibilityIdentifier("NavigateToFavorites")
-            
-            NavigationLink(destination: Settings()) {
-                Label("Settings", systemImage: "gear")
-            }
-            .accessibilityIdentifier("NavigateToSettings")
-        }
-        .listStyle(SidebarListStyle())
-        .navigationTitle("Categories")
-        .accessibilityIdentifier("Sidebar")
-    }
-}
-
-struct Sidebar_Previews: PreviewProvider {
-    static var previews: some View {
-        NavigationView {
-            Sidebar()
-        }
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/Helpers/SnapshotHelper.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -0,0 +1,309 @@
+//
+//  SnapshotHelper.swift
+//  Example
+//
+//  Created by Felix Krause on 10/8/15.
+//
+
+// -----------------------------------------------------
+// IMPORTANT: When modifying this file, make sure to
+//            increment the version number at the very
+//            bottom of the file to notify users about
+//            the new SnapshotHelper.swift
+// -----------------------------------------------------
+
+import Foundation
+import XCTest
+
+var deviceLanguage = ""
+var locale = ""
+
+func setupSnapshot(_ app: XCUIApplication, waitForAnimations: Bool = true) {
+    Snapshot.setupSnapshot(app, waitForAnimations: waitForAnimations)
+}
+
+func snapshot(_ name: String, waitForLoadingIndicator: Bool) {
+    if waitForLoadingIndicator {
+        Snapshot.snapshot(name)
+    } else {
+        Snapshot.snapshot(name, timeWaitingForIdle: 0)
+    }
+}
+
+/// - Parameters:
+///   - name: The name of the snapshot
+///   - timeout: Amount of seconds to wait until the network loading indicator disappears. Pass `0` if you don't want to wait.
+func snapshot(_ name: String, timeWaitingForIdle timeout: TimeInterval = 20) {
+    Snapshot.snapshot(name, timeWaitingForIdle: timeout)
+}
+
+enum SnapshotError: Error, CustomDebugStringConvertible {
+    case cannotFindSimulatorHomeDirectory
+    case cannotRunOnPhysicalDevice
+
+    var debugDescription: String {
+        switch self {
+        case .cannotFindSimulatorHomeDirectory:
+            return "Couldn't find simulator home location. Please, check SIMULATOR_HOST_HOME env variable."
+        case .cannotRunOnPhysicalDevice:
+            return "Can't use Snapshot on a physical device."
+        }
+    }
+}
+
+@objcMembers
+open class Snapshot: NSObject {
+    static var app: XCUIApplication?
+    static var waitForAnimations = true
+    static var cacheDirectory: URL?
+    static var screenshotsDirectory: URL? {
+        return cacheDirectory?.appendingPathComponent("screenshots", isDirectory: true)
+    }
+
+    open class func setupSnapshot(_ app: XCUIApplication, waitForAnimations: Bool = true) {
+
+        Snapshot.app = app
+        Snapshot.waitForAnimations = waitForAnimations
+
+        do {
+            let cacheDir = try getCacheDirectory()
+            Snapshot.cacheDirectory = cacheDir
+            setLanguage(app)
+            setLocale(app)
+            setLaunchArguments(app)
+        } catch let error {
+            NSLog(error.localizedDescription)
+        }
+    }
+
+    class func setLanguage(_ app: XCUIApplication) {
+        guard let cacheDirectory = self.cacheDirectory else {
+            NSLog("CacheDirectory is not set - probably running on a physical device?")
+            return
+        }
+
+        let path = cacheDirectory.appendingPathComponent("language.txt")
+
+        do {
+            let trimCharacterSet = CharacterSet.whitespacesAndNewlines
+            deviceLanguage = try String(contentsOf: path, encoding: .utf8).trimmingCharacters(in: trimCharacterSet)
+            app.launchArguments += ["-AppleLanguages", "(\(deviceLanguage))"]
+        } catch {
+            NSLog("Couldn't detect/set language...")
+        }
+    }
+
+    class func setLocale(_ app: XCUIApplication) {
+        guard let cacheDirectory = self.cacheDirectory else {
+            NSLog("CacheDirectory is not set - probably running on a physical device?")
+            return
+        }
+
+        let path = cacheDirectory.appendingPathComponent("locale.txt")
+
+        do {
+            let trimCharacterSet = CharacterSet.whitespacesAndNewlines
+            locale = try String(contentsOf: path, encoding: .utf8).trimmingCharacters(in: trimCharacterSet)
+        } catch {
+            NSLog("Couldn't detect/set locale...")
+        }
+
+        if locale.isEmpty && !deviceLanguage.isEmpty {
+            locale = Locale(identifier: deviceLanguage).identifier
+        }
+
+        if !locale.isEmpty {
+            app.launchArguments += ["-AppleLocale", "\"\(locale)\""]
+        }
+    }
+
+    class func setLaunchArguments(_ app: XCUIApplication) {
+        guard let cacheDirectory = self.cacheDirectory else {
+            NSLog("CacheDirectory is not set - probably running on a physical device?")
+            return
+        }
+
+        let path = cacheDirectory.appendingPathComponent("snapshot-launch_arguments.txt")
+        app.launchArguments += ["-FASTLANE_SNAPSHOT", "YES", "-ui_testing"]
+
+        do {
+            let launchArguments = try String(contentsOf: path, encoding: String.Encoding.utf8)
+            let regex = try NSRegularExpression(pattern: "(\\\".+?\\\"|\\S+)", options: [])
+            let matches = regex.matches(in: launchArguments, options: [], range: NSRange(location: 0, length: launchArguments.count))
+            let results = matches.map { result -> String in
+                (launchArguments as NSString).substring(with: result.range)
+            }
+            app.launchArguments += results
+        } catch {
+            NSLog("Couldn't detect/set launch_arguments...")
+        }
+    }
+
+    open class func snapshot(_ name: String, timeWaitingForIdle timeout: TimeInterval = 20) {
+        if timeout > 0 {
+            waitForLoadingIndicatorToDisappear(within: timeout)
+        }
+
+        NSLog("snapshot: \(name)") // more information about this, check out https://docs.fastlane.tools/actions/snapshot/#how-does-it-work
+
+        if Snapshot.waitForAnimations {
+            sleep(1) // Waiting for the animation to be finished (kind of)
+        }
+
+        #if os(OSX)
+            guard let app = self.app else {
+                NSLog("XCUIApplication is not set. Please call setupSnapshot(app) before snapshot().")
+                return
+            }
+
+            app.typeKey(XCUIKeyboardKeySecondaryFn, modifierFlags: [])
+        #else
+
+            guard self.app != nil else {
+                NSLog("XCUIApplication is not set. Please call setupSnapshot(app) before snapshot().")
+                return
+            }
+
+            let screenshot = XCUIScreen.main.screenshot()
+            #if os(iOS)
+            let image = XCUIDevice.shared.orientation.isLandscape ?  fixLandscapeOrientation(image: screenshot.image) : screenshot.image
+            #else
+            let image = screenshot.image
+            #endif
+
+            guard var simulator = ProcessInfo().environment["SIMULATOR_DEVICE_NAME"], let screenshotsDir = screenshotsDirectory else { return }
+
+            do {
+                // The simulator name contains "Clone X of " inside the screenshot file when running parallelized UI Tests on concurrent devices
+                let regex = try NSRegularExpression(pattern: "Clone [0-9]+ of ")
+                let range = NSRange(location: 0, length: simulator.count)
+                simulator = regex.stringByReplacingMatches(in: simulator, range: range, withTemplate: "")
+
+                let path = screenshotsDir.appendingPathComponent("\(simulator)-\(name).png")
+                #if swift(<5.0)
+                    UIImagePNGRepresentation(image)?.write(to: path, options: .atomic)
+                #else
+                    try image.pngData()?.write(to: path, options: .atomic)
+                #endif
+            } catch let error {
+                NSLog("Problem writing screenshot: \(name) to \(screenshotsDir)/\(simulator)-\(name).png")
+                NSLog(error.localizedDescription)
+            }
+        #endif
+    }
+
+    class func fixLandscapeOrientation(image: UIImage) -> UIImage {
+        #if os(watchOS)
+            return image
+        #else
+            if #available(iOS 10.0, *) {
+                let format = UIGraphicsImageRendererFormat()
+                format.scale = image.scale
+                let renderer = UIGraphicsImageRenderer(size: image.size, format: format)
+                return renderer.image { context in
+                    image.draw(in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))
+                }
+            } else {
+                return image
+            }
+        #endif
+    }
+
+    class func waitForLoadingIndicatorToDisappear(within timeout: TimeInterval) {
+        #if os(tvOS)
+            return
+        #endif
+
+        guard let app = self.app else {
+            NSLog("XCUIApplication is not set. Please call setupSnapshot(app) before snapshot().")
+            return
+        }
+
+        let networkLoadingIndicator = app.otherElements.deviceStatusBars.networkLoadingIndicators.element
+        let networkLoadingIndicatorDisappeared = XCTNSPredicateExpectation(predicate: NSPredicate(format: "exists == false"), object: networkLoadingIndicator)
+        _ = XCTWaiter.wait(for: [networkLoadingIndicatorDisappeared], timeout: timeout)
+    }
+
+    class func getCacheDirectory() throws -> URL {
+        let cachePath = "Library/Caches/tools.fastlane"
+        // on OSX config is stored in /Users/<username>/Library
+        // and on iOS/tvOS/WatchOS it's in simulator's home dir
+        #if os(OSX)
+            let homeDir = URL(fileURLWithPath: NSHomeDirectory())
+            return homeDir.appendingPathComponent(cachePath)
+        #elseif arch(i386) || arch(x86_64) || arch(arm64)
+            guard let simulatorHostHome = ProcessInfo().environment["SIMULATOR_HOST_HOME"] else {
+                throw SnapshotError.cannotFindSimulatorHomeDirectory
+            }
+            let homeDir = URL(fileURLWithPath: simulatorHostHome)
+            return homeDir.appendingPathComponent(cachePath)
+        #else
+            throw SnapshotError.cannotRunOnPhysicalDevice
+        #endif
+    }
+}
+
+private extension XCUIElementAttributes {
+    var isNetworkLoadingIndicator: Bool {
+        if hasAllowListedIdentifier { return false }
+
+        let hasOldLoadingIndicatorSize = frame.size == CGSize(width: 10, height: 20)
+        let hasNewLoadingIndicatorSize = frame.size.width.isBetween(46, and: 47) && frame.size.height.isBetween(2, and: 3)
+
+        return hasOldLoadingIndicatorSize || hasNewLoadingIndicatorSize
+    }
+
+    var hasAllowListedIdentifier: Bool {
+        let allowListedIdentifiers = ["GeofenceLocationTrackingOn", "StandardLocationTrackingOn"]
+
+        return allowListedIdentifiers.contains(identifier)
+    }
+
+    func isStatusBar(_ deviceWidth: CGFloat) -> Bool {
+        if elementType == .statusBar { return true }
+        guard frame.origin == .zero else { return false }
+
+        let oldStatusBarSize = CGSize(width: deviceWidth, height: 20)
+        let newStatusBarSize = CGSize(width: deviceWidth, height: 44)
+
+        return [oldStatusBarSize, newStatusBarSize].contains(frame.size)
+    }
+}
+
+private extension XCUIElementQuery {
+    var networkLoadingIndicators: XCUIElementQuery {
+        let isNetworkLoadingIndicator = NSPredicate { (evaluatedObject, _) in
+            guard let element = evaluatedObject as? XCUIElementAttributes else { return false }
+
+            return element.isNetworkLoadingIndicator
+        }
+
+        return self.containing(isNetworkLoadingIndicator)
+    }
+
+    var deviceStatusBars: XCUIElementQuery {
+        guard let app = Snapshot.app else {
+            fatalError("XCUIApplication is not set. Please call setupSnapshot(app) before snapshot().")
+        }
+
+        let deviceWidth = app.windows.firstMatch.frame.width
+
+        let isStatusBar = NSPredicate { (evaluatedObject, _) in
+            guard let element = evaluatedObject as? XCUIElementAttributes else { return false }
+
+            return element.isStatusBar(deviceWidth)
+        }
+
+        return self.containing(isStatusBar)
+    }
+}
+
+private extension CGFloat {
+    func isBetween(_ numberA: CGFloat, and numberB: CGFloat) -> Bool {
+        return numberA...numberB ~= self
+    }
+}
+
+// Please don't remove the lines below
+// They are used to detect outdated configuration files
+// SnapshotHelperVersion [1.27]
--- a/Simoleon/Helpers/SubscribeButton.swift	Wed Aug 25 11:00:21 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-//
-//  SubscribeButton.swift
-//  Simoleon
-//
-//  Created by Dennis Concepción Martín on 22/07/2021.
-//
-
-import SwiftUI
-import Purchases
-
-struct SubscribeButton: View {
-    @Binding var showingSubscriptionPaywall: Bool
-    
-    @State private var price = ""
-    @State private var alertTitle = ""
-    @State private var alertMessage = ""
-    @State private var showingAlert = false
-    @State private var showingPrice = false
-    
-    var body: some View {
-        Button(action: purchaseMonthlySubscription) {
-            RoundedRectangle(cornerRadius: 15)
-                .frame(height: 60)
-                .overlay(
-                    VStack {
-                        if showingPrice {
-                            Text("Subscribe for \(price) / month")
-                                .foregroundColor(.white)
-                                .fontWeight(.semibold)
-                        } else {
-                            ProgressView()
-                        }
-                    }
-                )
-        }
-        .onAppear(perform: fetchMonthlySubscription)
-        .alert(isPresented: $showingAlert) {
-            Alert(title: Text(alertTitle), message: Text(alertMessage), dismissButton: .default(Text("Ok")))
-        }
-    }
-    
-    private func fetchMonthlySubscription() {
-        Purchases.shared.offerings { (offerings, error) in
-            if let product = offerings?.current?.monthly?.product {
-                price = formatCurrency(product.priceLocale, product.price)
-                showingPrice = true
-            }
-            
-            if let error = error as NSError? {
-                alertTitle = error.localizedDescription
-                alertMessage = error.localizedFailureReason ?? ""
-                price = "-"
-                showingPrice = true
-                showingAlert = true
-            }
-        }
-    }
-    
-    private func purchaseMonthlySubscription() {
-        showingPrice = false
-        
-        Purchases.shared.offerings { (offerings, error) in
-            if let package = offerings?.current?.monthly {
-                
-                Purchases.shared.purchasePackage(package) { (transaction, purchaserInfo, error, userCancelled) in
-                    if purchaserInfo?.entitlements["all"]?.isActive == true {
-                        showingPrice = true
-                        showingSubscriptionPaywall = false
-                    }
-                    
-                    if let error = error as NSError? {
-                        alertTitle = error.localizedDescription
-                        alertMessage = error.localizedFailureReason ?? ""
-                        showingPrice = true
-                        showingAlert = true
-                    }
-                }
-                
-                if let error = error as NSError? {
-                    alertTitle = error.localizedDescription
-                    alertMessage = error.localizedFailureReason ?? ""
-                    showingPrice = true
-                    showingAlert = true
-                }
-            }
-        }
-    }
-    
-    private func formatCurrency(_ locale: Locale, _ amount: NSDecimalNumber) -> String {
-        let formatter = NumberFormatter()
-        formatter.locale = locale
-        formatter.numberStyle = .currency
-        
-        // It won't fail. Check unit test
-        let formattedAmount = formatter.string(from: amount as NSNumber)!
-        
-        return formattedAmount
-    }
-}
-
-struct SubscribeButton_Previews: PreviewProvider {
-    static var previews: some View {
-        SubscribeButton(showingSubscriptionPaywall: .constant(true))
-    }
-}
--- a/Simoleon/Helpers/SubscriptionFeature.swift	Wed Aug 25 11:00:21 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-//
-//  SubscriptionFeature.swift
-//  Simoleon
-//
-//  Created by Dennis Concepción Martín on 26/07/2021.
-//
-
-import SwiftUI
-
-struct SubscriptionFeature: View {
-    var symbol: String
-    var colour: Color
-    var title: LocalizedStringKey
-    var description: LocalizedStringKey
-    
-    var body: some View {
-        HStack(alignment:.top) {
-            Image(systemName: symbol)
-                .foregroundColor(colour)
-                .font(.title)
-            
-            VStack(alignment: .leading) {
-                Text(title)
-                    .font(.headline)
-                
-                Text(description)
-            }
-        }
-    }
-}
-
-struct SubscriptionFeature_Previews: PreviewProvider {
-    static var previews: some View {
-        SubscriptionFeature(
-            symbol: "star.circle.fill",
-            colour: Color(.systemYellow),
-            title: "Favorite Currencies",
-            description: "Save your favorite currencies to access them quickly."
-        )
-    }
-}
--- a/Simoleon/Jobs/CurrenciesController.swift	Wed Aug 25 11:00:21 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-//
-//  GetCompatibleCurrencies.swift
-//  Simoleon
-//
-//  Created by Dennis Concepción Martín on 24/8/21.
-//
-
-import Foundation
-
-class CurrenciesController {
-    let fileController = FileController()
-    
-    func get(currenciesCompatibleWith currencySymbol: String?, currencies: Bool?) -> [String] {
-        // If currencies not false -> return all currencies
-        guard currencies == false else { return allCurrencies() }
-        
-        // This block won't be executed if the previous check fails
-        return compatibleCurrencies(with: currencySymbol!)
-    }
-    
-    /*
-     * Input all currencies supported by vendor
-     * Return individual currency symbols without duplicates
-     */
-    private func allCurrencies() -> [String] {
-        let currencyPairsSupported: [String] = try! fileController.read(json: "CurrencyPairsSupported.json")
-        
-        var currencies = Set<String>()
-        for currencyPairSupported in currencyPairsSupported {
-            let currency = currencyPairSupported.components(separatedBy: "/")[0]
-            currencies.insert(currency)
-        }
-        
-        return Array(currencies)
-    }
-    
-    /*
-     * Given the first symbol of the currency pair
-     * Return all compatible symbols
-     */
-    private func compatibleCurrencies(with currencySymbol: String) -> [String] {
-        let currencyPairsSupported: [String] = try! fileController.read(json: "CurrencyPairsSupported.json")
-        
-        var currencies = [String]()
-        for currencyPairSupported in currencyPairsSupported {
-            if currencyPairSupported.hasPrefix(currencySymbol) {
-                let compatibleCurrency = currencyPairSupported.components(separatedBy: "/")[1]
-                currencies.append(compatibleCurrency)
-            }
-        }
-        
-        return currencies
-    }
-}
--- a/Simoleon/Jobs/FileController.swift	Wed Aug 25 11:00:21 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-//
-//  ReadConfig.swift
-//  Simoleon
-//
-//  Created by Dennis Concepción Martín on 20/07/2021.
-//
-
-import Foundation
-
-class FileController {
-    
-    /*
-     Read configuration variables from Config.xconfig
-     */
-    func readConfigVariable(withKey: String) -> String? {
-        return (Bundle.main.infoDictionary?[withKey] as? String)?
-            .replacingOccurrences(of: "\\", with: "")
-    }
-    
-    /*
-     Decode and read json file
-     */
-    func read<T: Decodable>(json filename: String) throws -> T {
-        let data: Data
-        
-        guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
-        else {
-            throw ErrorHandling.Json.fileMissing
-        }
-        
-        do {
-            data = try Data(contentsOf: file)
-        } catch {
-            throw ErrorHandling.Json.loadFailed(cause: error.localizedDescription)
-        }
-        
-        do {
-            let decoder = JSONDecoder()
-            return try decoder.decode(T.self, from: data)
-        } catch {
-            throw ErrorHandling.Json.parseFailed(cause: error.localizedDescription)
-        }
-    }
-}
--- a/Simoleon/Jobs/HapticsController.swift	Wed Aug 25 11:00:21 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-//
-//  SimpleSuccess.swift
-//  Simoleon
-//
-//  Created by Dennis Concepción Martín on 20/07/2021.
-//
-
-import SwiftUI
-
-class HapticsController {
-    
-    /*
-     Default haptic for success action
-     */
-    func simpleSuccess() {
-        let generator = UINotificationFeedbackGenerator()
-        generator.notificationOccurred(.success)
-    }
-}
--- a/Simoleon/Jobs/NetworkController.swift	Wed Aug 25 11:00:21 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-//
-//  Request.swift
-//  Simoleon
-//
-//  Created by Dennis Concepción Martín on 20/07/2021.
-//
-
-import Foundation
-
-class NetworkController {
-    
-    /*
-     Get http response and decode it with specified model
-     */
-    func httpRequest<T: Decodable>(url: String, model: T.Type, completion: @escaping (_ result: T) -> Void) {
-        
-        // We take some model data T.Type
-        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()
-    }
-}
--- a/Simoleon/Models/CurrencyDetailsModel.swift	Wed Aug 25 11:00:21 2021 +0100
+++ b/Simoleon/Models/CurrencyDetailsModel.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -7,7 +7,8 @@
 
 import Foundation
 
-struct CurrencyDetailsModel: Codable {
+struct CurrencyModel: Codable {
+    var symbol: String
     var name: String
     var flag: String
     var isCrypto: Bool
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/Models/CurrencyPairModel.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -0,0 +1,18 @@
+//
+//  CurrencyPairModel.swift
+//  Simoleon
+//
+//  Created by Dennis Concepción Martín on 26/8/21.
+//
+
+import Foundation
+
+struct CurrencyPairModel {
+    /*
+     Forex pair -> XXX/YYY
+     Where XXX is the base currency, and YYY the quote currency
+     */
+    
+    var baseSymbol: String
+    var quoteSymbol: String
+}
--- a/Simoleon/Models/DefaultCurrency+CoreDataClass.swift	Wed Aug 25 11:00:21 2021 +0100
+++ b/Simoleon/Models/DefaultCurrency+CoreDataClass.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -2,7 +2,7 @@
 //  DefaultCurrency+CoreDataClass.swift
 //  Simoleon
 //
-//  Created by Dennis Concepción Martín on 21/07/2021.
+//  Created by Dennis Concepción Martín on 24/8/21.
 //
 //
 
@@ -11,5 +11,5 @@
 
 @objc(DefaultCurrency)
 public class DefaultCurrency: NSManagedObject {
-    
+
 }
--- a/Simoleon/Models/DefaultCurrency+CoreDataProperties.swift	Wed Aug 25 11:00:21 2021 +0100
+++ b/Simoleon/Models/DefaultCurrency+CoreDataProperties.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -2,7 +2,7 @@
 //  DefaultCurrency+CoreDataProperties.swift
 //  Simoleon
 //
-//  Created by Dennis Concepción Martín on 21/07/2021.
+//  Created by Dennis Concepción Martín on 24/8/21.
 //
 //
 
@@ -11,11 +11,16 @@
 
 
 extension DefaultCurrency {
-    
+
     @nonobjc public class func fetchRequest() -> NSFetchRequest<DefaultCurrency> {
         return NSFetchRequest<DefaultCurrency>(entityName: "DefaultCurrency")
     }
-    
-    @NSManaged public var pair: String?
-    
+
+    @NSManaged public var firstSymbol: String
+    @NSManaged public var secondSymbol: String
+
 }
+
+extension DefaultCurrency : Identifiable {
+
+}
--- a/Simoleon/Models/Favorite+CoreDataClass.swift	Wed Aug 25 11:00:21 2021 +0100
+++ b/Simoleon/Models/Favorite+CoreDataClass.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -2,7 +2,7 @@
 //  Favorite+CoreDataClass.swift
 //  Simoleon
 //
-//  Created by Dennis Concepción Martín on 19/07/2021.
+//  Created by Dennis Concepción Martín on 24/8/21.
 //
 //
 
@@ -11,5 +11,5 @@
 
 @objc(Favorite)
 public class Favorite: NSManagedObject {
-    
+
 }
--- a/Simoleon/Models/Favorite+CoreDataProperties.swift	Wed Aug 25 11:00:21 2021 +0100
+++ b/Simoleon/Models/Favorite+CoreDataProperties.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -2,7 +2,7 @@
 //  Favorite+CoreDataProperties.swift
 //  Simoleon
 //
-//  Created by Dennis Concepción Martín on 19/07/2021.
+//  Created by Dennis Concepción Martín on 24/8/21.
 //
 //
 
@@ -11,15 +11,15 @@
 
 
 extension Favorite {
-    
+
     @nonobjc public class func fetchRequest() -> NSFetchRequest<Favorite> {
         return NSFetchRequest<Favorite>(entityName: "Favorite")
     }
-    
+
     @NSManaged public var currencyPair: String
-    
+
 }
 
 extension Favorite : Identifiable {
-    
+
 }
--- a/Simoleon/Persistence.swift	Wed Aug 25 11:00:21 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-//
-//  Persistence.swift
-//  Simoleon
-//
-//  Created by Dennis Concepción Martín on 08/07/2021.
-//
-
-import CoreData
-
-struct PersistenceController {
-    static let shared = PersistenceController()
-    
-    static var preview: PersistenceController = {
-        let result = PersistenceController(inMemory: true)
-        let viewContext = result.container.viewContext
-        
-        for _ in 0..<10 {
-            let favorite = Favorite(context: viewContext)
-            favorite.currencyPair = "USD/GBP"
-        }
-        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)")
-        }
-        return result
-    }()
-    
-    let container: NSPersistentCloudKitContainer
-    
-    init(inMemory: Bool = false) {
-        container = NSPersistentCloudKitContainer(name: "Simoleon")
-        container.viewContext.automaticallyMergesChangesFromParent = true
-        container.viewContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy
-        if inMemory {
-            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
-        }
-        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
-            if let error = error as NSError? {
-                /*
-                 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.
-                 */
-                
-                /*
-                 Typical reasons for an error here include:
-                 * The parent directory does not exist, cannot be created, or disallows writing.
-                 * The persistent store is not accessible, due to permissions or data protection when the device is locked.
-                 * The device is out of space.
-                 * The store could not be migrated to the current model version.
-                 Check the error message to determine what the actual problem was.
-                 */
-                fatalError("Unresolved error \(error), \(error.userInfo)")
-            }
-        })
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/Resources/Currencies.json	Sat Aug 28 11:15:25 2021 +0100
@@ -0,0 +1,1374 @@
+{
+    "AED":
+    {
+        "symbol": "AED",
+        "name": "United Arab Emirates Dirham",
+        "flag": "AE",
+        "isCrypto": false
+    },
+    "AFN":
+    {
+        "symbol": "AFN",
+        "name": "Afghan Afghani",
+        "flag": "AF",
+        "isCrypto": false
+    },
+    "ALL":
+    {
+        "symbol": "ALL",
+        "name": "Albanian Lek",
+        "flag": "AL",
+        "isCrypto": false
+    },
+    "AMD":
+    {
+        "symbol": "AMD",
+        "name": "Armenian Dram",
+        "flag": "AM",
+        "isCrypto": false
+    },
+    "ANG":
+    {
+        "symbol": "ANG",
+        "name": "Netherlands Antillean Guilder",
+        "flag": "CW",
+        "isCrypto": false
+    },
+    "AOA":
+    {
+        "symbol": "AOA",
+        "name": "Angolan Kwanza",
+        "flag": "AO",
+        "isCrypto": false
+    },
+    "ARE":
+    {
+        "symbol": "ARE",
+        "name": "Areg",
+        "flag": "AR",
+        "isCrypto": false
+    },
+    "ARS":
+    {
+        "symbol": "ARS",
+        "name": "Argentine Peso",
+        "flag": "AR",
+        "isCrypto": false
+    },
+    "AUD":
+    {
+        "symbol": "AUD",
+        "name": "Australian Dollar",
+        "flag": "AU",
+        "isCrypto": false
+    },
+    "AUN":
+    {
+        "symbol": "AUN",
+        "name": "Australian Nugget",
+        "flag": "AU",
+        "isCrypto": false
+    },
+    "AWG":
+    {
+        "symbol": "AWG",
+        "name": "Aruban Florin",
+        "flag": "AW",
+        "isCrypto": false
+    },
+    "BAM":
+    {
+        "symbol": "BAM",
+        "name": "Bosnia And Herzegovina Convertible Mark",
+        "flag": "BA",
+        "isCrypto": false
+    },
+    "BBD":
+    {
+        "symbol": "BBD",
+        "name": "Barbados Dollar",
+        "flag": "BB",
+        "isCrypto": false
+    },
+    "BDT":
+    {
+        "symbol": "BDT",
+        "name": "Bangladeshi Taka",
+        "flag": "BD",
+        "isCrypto": false
+    },
+    "BGN":
+    {
+        "symbol": "BGN",
+        "name": "Bulgarian Lev",
+        "flag": "BG",
+        "isCrypto": false
+    },
+    "BHD":
+    {
+        "symbol": "BHD",
+        "name": "Bahraini Dinar",
+        "flag": "BH",
+        "isCrypto": false
+    },
+    "BIF":
+    {
+        "symbol": "BIF",
+        "name": "Burundian Franc",
+        "flag": "BI",
+        "isCrypto": false
+    },
+    "BMD":
+    {
+        "symbol": "BMD",
+        "name": "Bermudian Dollar",
+        "flag": "BM",
+        "isCrypto": false
+    },
+    "BND":
+    {
+        "symbol": "BND",
+        "name": "Brunei Dollar",
+        "flag": "BN",
+        "isCrypto": false
+    },
+    "BOB":
+    {
+        "symbol": "BOB",
+        "name": "Boliviano",
+        "flag": "BO",
+        "isCrypto": false
+    },
+    "BRI":
+    {
+        "symbol": "BRI",
+        "name": "Britannia",
+        "flag": "BR",
+        "isCrypto": false
+    },
+    "BRL":
+    {
+        "symbol": "BRL",
+        "name": "Brazilian Real",
+        "flag": "BR",
+        "isCrypto": false
+    },
+    "BSD":
+    {
+        "symbol": "BSD",
+        "name": "Bahamian Dollar",
+        "flag": "BS",
+        "isCrypto": false
+    },
+    "BTN":
+    {
+        "symbol": "BTN",
+        "name": "Bhutanese Ngultrum",
+        "flag": "BT",
+        "isCrypto": false
+    },
+    "BWP":
+    {
+        "symbol": "BWP",
+        "name": "Botswana Pula",
+        "flag": "BW",
+        "isCrypto": false
+    },
+    "BYN":
+    {
+        "symbol": "BYN",
+        "name": "Belarusian Ruble",
+        "flag": "BY",
+        "isCrypto": false
+    },
+    "BZD":
+    {
+        "symbol": "BZD",
+        "name": "Belize Dollar",
+        "flag": "BZ",
+        "isCrypto": false
+    },
+    "CAD":
+    {
+        "symbol": "CAD",
+        "name": "Canadian Dollar",
+        "flag": "CA",
+        "isCrypto": false
+    },
+    "CDF":
+    {
+        "symbol": "CDF",
+        "name": "Congolese Franc",
+        "flag": "CD",
+        "isCrypto": false
+    },
+    "CHF":
+    {
+        "symbol": "CHF",
+        "name": "Swiss Franc",
+        "flag": "CH",
+        "isCrypto": false
+    },
+    "CLF":
+    {
+        "symbol": "CLF",
+        "name": "Chilean Unit Of Account",
+        "flag": "CL",
+        "isCrypto": false
+    },
+    "CLP":
+    {
+        "symbol": "CLP",
+        "name": "Chilean Peso",
+        "flag": "CL",
+        "isCrypto": false
+    },
+    "CNH":
+    {
+        "symbol": "CNH",
+        "name": "Chinese Yuan",
+        "flag": "CN",
+        "isCrypto": false
+    },
+    "CNY":
+    {
+        "symbol": "CNY",
+        "name": "Chinese Yuan",
+        "flag": "CN",
+        "isCrypto": false
+    },
+    "COP":
+    {
+        "symbol": "COP",
+        "name": "Colombian Peso",
+        "flag": "CO",
+        "isCrypto": false
+    },
+    "CRC":
+    {
+        "symbol": "CRC",
+        "name": "Costa Rican Colon",
+        "flag": "CR",
+        "isCrypto": false
+    },
+    "CUP":
+    {
+        "symbol": "CUP",
+        "name": "Cuban Peso",
+        "flag": "CU",
+        "isCrypto": false
+    },
+    "CVE":
+    {
+        "symbol": "CVE",
+        "name": "Cape Verde Escudo",
+        "flag": "CV",
+        "isCrypto": false
+    },
+    "CYP":
+    {
+        "symbol": "CYP",
+        "name": "Cypriot Pound",
+        "flag": "CY",
+        "isCrypto": false
+    },
+    "CZK":
+    {
+        "symbol": "CZK",
+        "name": "Czech Koruna",
+        "flag": "CZ",
+        "isCrypto": false
+    },
+    "DJF":
+    {
+        "symbol": "DJF",
+        "name": "Djiboutian Franc",
+        "flag": "DJ",
+        "isCrypto": false
+    },
+    "DKK":
+    {
+        "symbol": "DKK",
+        "name": "Danish Krone",
+        "flag": "DK",
+        "isCrypto": false
+    },
+    "DOE":
+    {
+        "symbol": "DOE",
+        "name": "Double Eagle",
+        "flag": "DO",
+        "isCrypto": false
+    },
+    "DOP":
+    {
+        "symbol": "DOP",
+        "name": "Dominican Peso",
+        "flag": "DO",
+        "isCrypto": false
+    },
+    "DZD":
+    {
+        "symbol": "DZD",
+        "name": "Algerian Dinar",
+        "flag": "DZ",
+        "isCrypto": false
+    },
+    "EGP":
+    {
+        "symbol": "EGP",
+        "name": "Egyptian Pound",
+        "flag": "EG",
+        "isCrypto": false
+    },
+    "ETB":
+    {
+        "symbol": "ETB",
+        "name": "Ethiopian Birr",
+        "flag": "ET",
+        "isCrypto": false
+    },
+    "EUR":
+    {
+        "symbol": "EUR",
+        "name": "Euro",
+        "flag": "EU",
+        "isCrypto": false
+    },
+    "FJD":
+    {
+        "symbol": "FJD",
+        "name": "Fiji Dollar",
+        "flag": "FJ",
+        "isCrypto": false
+    },
+    "FRN":
+    {
+        "symbol": "FRN",
+        "name": "French Napoleon",
+        "flag": "FR",
+        "isCrypto": false
+    },
+    "GBP":
+    {
+        "symbol": "GBP",
+        "name": "Pound Sterling",
+        "flag": "GB",
+        "isCrypto": false
+    },
+    "GEL":
+    {
+        "symbol": "GEL",
+        "name": "Georgian Lari",
+        "flag": "GE",
+        "isCrypto": false
+    },
+    "GHS":
+    {
+        "symbol": "GHS",
+        "name": "Ghanaian Cedi",
+        "flag": "GH",
+        "isCrypto": false
+    },
+    "GMD":
+    {
+        "symbol": "GMD",
+        "name": "Gambian Dalasi",
+        "flag": "GM",
+        "isCrypto": false
+    },
+    "GNF":
+    {
+        "symbol": "GNF",
+        "name": "Guinean Franc",
+        "flag": "GN",
+        "isCrypto": false
+    },
+    "GTQ":
+    {
+        "symbol": "GTQ",
+        "name": "Guatemalan Quetzal",
+        "flag": "GT",
+        "isCrypto": false
+    },
+    "GYD":
+    {
+        "symbol": "GYD",
+        "name": "Guyanese Dollar",
+        "flag": "GY",
+        "isCrypto": false
+    },
+    "HKD":
+    {
+        "symbol": "HKD",
+        "name": "Hong Kong Dollar",
+        "flag": "HK",
+        "isCrypto": false
+    },
+    "HNL":
+    {
+        "symbol": "HNL",
+        "name": "Honduran Lempira",
+        "flag": "HN",
+        "isCrypto": false
+    },
+    "HRK":
+    {
+        "symbol": "HRK",
+        "name": "Croatian Kuna",
+        "flag": "HR",
+        "isCrypto": false
+    },
+    "HTG":
+    {
+        "symbol": "HTG",
+        "name": "Haitian Gourde",
+        "flag": "HT",
+        "isCrypto": false
+    },
+    "HUF":
+    {
+        "symbol": "HUF",
+        "name": "Hungarian Forint",
+        "flag": "HU",
+        "isCrypto": false
+    },
+    "IDR":
+    {
+        "symbol": "IDR",
+        "name": "Indonesian Rupiah",
+        "flag": "ID",
+        "isCrypto": false
+    },
+    "ILS":
+    {
+        "symbol": "ILS",
+        "name": "Israeli New Shekel",
+        "flag": "IL",
+        "isCrypto": false
+    },
+    "INR":
+    {
+        "symbol": "INR",
+        "name": "Indian Rupee",
+        "flag": "IN",
+        "isCrypto": false
+    },
+    "IQD":
+    {
+        "symbol": "IQD",
+        "name": "Iraqi Dinar",
+        "flag": "IQ",
+        "isCrypto": false
+    },
+    "IRR":
+    {
+        "symbol": "IRR",
+        "name": "Iranian Rial",
+        "flag": "IR",
+        "isCrypto": false
+    },
+    "ISK":
+    {
+        "symbol": "ISK",
+        "name": "Icelandic Króna",
+        "flag": "IS",
+        "isCrypto": false
+    },
+    "JMD":
+    {
+        "symbol": "JMD",
+        "name": "Jamaican Dollar",
+        "flag": "JM",
+        "isCrypto": false
+    },
+    "JOD":
+    {
+        "symbol": "JOD",
+        "name": "Jordanian Dinar",
+        "flag": "JO",
+        "isCrypto": false
+    },
+    "JPY":
+    {
+        "symbol": "JPY",
+        "name": "Japanese Yen",
+        "flag": "JP",
+        "isCrypto": false
+    },
+    "KES":
+    {
+        "symbol": "KES",
+        "name": "Kenyan Shilling",
+        "flag": "KE",
+        "isCrypto": false
+    },
+    "KHR":
+    {
+        "symbol": "KHR",
+        "name": "Cambodian Riel",
+        "flag": "KH",
+        "isCrypto": false
+    },
+    "KMF":
+    {
+        "symbol": "KMF",
+        "name": "Comoro Franc",
+        "flag": "KM",
+        "isCrypto": false
+    },
+    "KRU":
+    {
+        "symbol": "KRU",
+        "name": "South African Krugerrand",
+        "flag": "KR",
+        "isCrypto": false
+    },
+    "KRW":
+    {
+        "symbol": "KRW",
+        "name": "South Korean Won",
+        "flag": "KR",
+        "isCrypto": false
+    },
+    "KWD":
+    {
+        "symbol": "KWD",
+        "name": "Kuwaiti Dinar",
+        "flag": "KW",
+        "isCrypto": false
+    },
+    "KYD":
+    {
+        "symbol": "KYD",
+        "name": "Cayman Islands Dollar",
+        "flag": "KY",
+        "isCrypto": false
+    },
+    "KZT":
+    {
+        "symbol": "KZT",
+        "name": "Kazakhstani Tenge",
+        "flag": "KZ",
+        "isCrypto": false
+    },
+    "LAK":
+    {
+        "symbol": "LAK",
+        "name": "Lao Kip",
+        "flag": "LA",
+        "isCrypto": false
+    },
+    "LBP":
+    {
+        "symbol": "LBP",
+        "name": "Lebanese Pound",
+        "flag": "LB",
+        "isCrypto": false
+    },
+    "LFX":
+    {
+        "symbol": "LFX",
+        "name": "Khazanah Sukuk",
+        "flag": "MY",
+        "isCrypto": false
+    },
+    "LKR":
+    {
+        "symbol": "LKR",
+        "name": "Sri Lankan Rupee",
+        "flag": "LK",
+        "isCrypto": false
+    },
+    "LRD":
+    {
+        "symbol": "LRD",
+        "name": "Liberian Dollar",
+        "flag": "LR",
+        "isCrypto": false
+    },
+    "LSL":
+    {
+        "symbol": "LSL",
+        "name": "Lesotho Loti",
+        "flag": "LS",
+        "isCrypto": false
+    },
+    "LTL":
+    {
+        "symbol": "LTL",
+        "name": "Lithuanian Litas",
+        "flag": "LT",
+        "isCrypto": false
+    },
+    "LYD":
+    {
+        "symbol": "LYD",
+        "name": "Libyan Dinar",
+        "flag": "LY",
+        "isCrypto": false
+    },
+    "M5P":
+    {
+        "symbol": "M5P",
+        "name": "Mexican 50 Peso",
+        "flag": "MX",
+        "isCrypto": false
+    },
+    "MAD":
+    {
+        "symbol": "MAD",
+        "name": "Moroccan Dirham",
+        "flag": "MA",
+        "isCrypto": false
+    },
+    "MAL":
+    {
+        "symbol": "MAL",
+        "name": "Maple Leaf",
+        "flag": "MA",
+        "isCrypto": false
+    },
+    "MDL":
+    {
+        "symbol": "MDL",
+        "name": "Moldovan Leu",
+        "flag": "MD",
+        "isCrypto": false
+    },
+    "MGA":
+    {
+        "symbol": "MGA",
+        "name": "Malagasy Ariary",
+        "flag": "MG",
+        "isCrypto": false
+    },
+    "MKD":
+    {
+        "symbol": "MKD",
+        "name": "Macedonian Denar",
+        "flag": "MK",
+        "isCrypto": false
+    },
+    "MMK":
+    {
+        "symbol": "MMK",
+        "name": "Myanma Kyat",
+        "flag": "MM",
+        "isCrypto": false
+    },
+    "MOP":
+    {
+        "symbol": "MOP",
+        "name": "Macanese Pataca",
+        "flag": "MO",
+        "isCrypto": false
+    },
+    "MRU":
+    {
+        "symbol": "MRU",
+        "name": "Mauritanian Ouguiya",
+        "flag": "MR",
+        "isCrypto": false
+    },
+    "MTL":
+    {
+        "symbol": "MTL",
+        "name": "Maltese Lira",
+        "flag": "MT",
+        "isCrypto": false
+    },
+    "MUR":
+    {
+        "symbol": "MUR",
+        "name": "Mauritian Rupee",
+        "flag": "MU",
+        "isCrypto": false
+    },
+    "MVR":
+    {
+        "symbol": "MVR",
+        "name": "Maldivian Rufiyaa",
+        "flag": "MV",
+        "isCrypto": false
+    },
+    "MWK":
+    {
+        "symbol": "MWK",
+        "name": "Malawian Kwacha",
+        "flag": "MW",
+        "isCrypto": false
+    },
+    "MXN":
+    {
+        "symbol": "MXN",
+        "name": "Mexican Peso",
+        "flag": "MX",
+        "isCrypto": false
+    },
+    "MYR":
+    {
+        "symbol": "MYR",
+        "name": "Malaysian Ringgit",
+        "flag": "MY",
+        "isCrypto": false
+    },
+    "MZN":
+    {
+        "symbol": "MZN",
+        "name": "Mozambican Metical",
+        "flag": "MZ",
+        "isCrypto": false
+    },
+    "NAD":
+    {
+        "symbol": "NAD",
+        "name": "Namibian Dollar",
+        "flag": "NA",
+        "isCrypto": false
+    },
+    "NBL":
+    {
+        "symbol": "NBL",
+        "name": "Isle Of Man Noble",
+        "flag": "IM",
+        "isCrypto": false
+    },
+    "NGN":
+    {
+        "symbol": "NGN",
+        "name": "Nigerian Naira",
+        "flag": "NG",
+        "isCrypto": false
+    },
+    "NIO":
+    {
+        "symbol": "NIO",
+        "name": "Nicaraguan Córdoba",
+        "flag": "NI",
+        "isCrypto": false
+    },
+    "NOK":
+    {
+        "symbol": "NOK",
+        "name": "Norwegian Krone",
+        "flag": "NO",
+        "isCrypto": false
+    },
+    "NPR":
+    {
+        "symbol": "NPR",
+        "name": "Nepalese Rupee",
+        "flag": "NP",
+        "isCrypto": false
+    },
+    "NSO":
+    {
+        "symbol": "NSO",
+        "name": "New Sovereign",
+        "flag": "GB",
+        "isCrypto": false
+    },
+    "NZD":
+    {
+        "symbol": "NZD",
+        "name": "New Zealand Dollar",
+        "flag": "NZ",
+        "isCrypto": false
+    },
+    "OMR":
+    {
+        "symbol": "OMR",
+        "name": "Omani Rial",
+        "flag": "OM",
+        "isCrypto": false
+    },
+    "OSO":
+    {
+        "symbol": "OSO",
+        "name": "Old Sovereign",
+        "flag": "GB",
+        "isCrypto": false
+    },
+    "PAB":
+    {
+        "symbol": "PAB",
+        "name": "Panamanian Balboa",
+        "flag": "PA",
+        "isCrypto": false
+    },
+    "PEN":
+    {
+        "symbol": "PEN",
+        "name": "Peruvian Nuevo Sol",
+        "flag": "PE",
+        "isCrypto": false
+    },
+    "PGK":
+    {
+        "symbol": "PGK",
+        "name": "Papua New Guinean Kina",
+        "flag": "PG",
+        "isCrypto": false
+    },
+    "PHP":
+    {
+        "symbol": "PHP",
+        "name": "Philippine Peso",
+        "flag": "PH",
+        "isCrypto": false
+    },
+    "PKR":
+    {
+        "symbol": "PKR",
+        "name": "Pakistani Rupee",
+        "flag": "PK",
+        "isCrypto": false
+    },
+    "PLN":
+    {
+        "symbol": "PLN",
+        "name": "Polish Zloty",
+        "flag": "PL",
+        "isCrypto": false
+    },
+    "PYG":
+    {
+        "symbol": "PYG",
+        "name": "Paraguayan Guaraní",
+        "flag": "PY",
+        "isCrypto": false
+    },
+    "QAR":
+    {
+        "symbol": "QAR",
+        "name": "Qatari Riyal",
+        "flag": "QA",
+        "isCrypto": false
+    },
+    "RON":
+    {
+        "symbol": "RON",
+        "name": "Romanian New Leu",
+        "flag": "RO",
+        "isCrypto": false
+    },
+    "RSD":
+    {
+        "symbol": "RSD",
+        "name": "Serbian Dinar",
+        "flag": "RS",
+        "isCrypto": false
+    },
+    "RUB":
+    {
+        "symbol": "RUB",
+        "name": "Russian Rouble",
+        "flag": "RU",
+        "isCrypto": false
+    },
+    "RWF":
+    {
+        "symbol": "RWF",
+        "name": "Rwandan Franc",
+        "flag": "RW",
+        "isCrypto": false
+    },
+    "SAR":
+    {
+        "symbol": "SAR",
+        "name": "Saudi Riyal",
+        "flag": "SA",
+        "isCrypto": false
+    },
+    "SBD":
+    {
+        "symbol": "SBD",
+        "name": "Solomon Islands Dollar",
+        "flag": "SB",
+        "isCrypto": false
+    },
+    "SCR":
+    {
+        "symbol": "SCR",
+        "name": "Seychelles Rupee",
+        "flag": "SC",
+        "isCrypto": false
+    },
+    "SDG":
+    {
+        "symbol": "SDG",
+        "name": "Sudanese Pound",
+        "flag": "SD",
+        "isCrypto": false
+    },
+    "SEK":
+    {
+        "symbol": "SEK",
+        "name": "Swedish Krona",
+        "flag": "SE",
+        "isCrypto": false
+    },
+    "SGD":
+    {
+        "symbol": "SGD",
+        "name": "Singapore Dollar",
+        "flag": "SG",
+        "isCrypto": false
+    },
+    "SHP":
+    {
+        "symbol": "SHP",
+        "name": "Saint Helena Pound",
+        "flag": "SH",
+        "isCrypto": false
+    },
+    "SLL":
+    {
+        "symbol": "SLL",
+        "name": "Sierra Leonean Leone",
+        "flag": "SL",
+        "isCrypto": false
+    },
+    "SOS":
+    {
+        "symbol": "SOS",
+        "name": "Somali Shilling",
+        "flag": "SO",
+        "isCrypto": false
+    },
+    "SRD":
+    {
+        "symbol": "SRD",
+        "name": "Surinamese Dollar",
+        "flag": "SR",
+        "isCrypto": false
+    },
+    "STN":
+    {
+        "symbol": "STN",
+        "name": "Sao Tomean Dobra",
+        "flag": "ST",
+        "isCrypto": false
+    },
+    "SVC":
+    {
+        "symbol": "SVC",
+        "name": "Salvadoran Colón",
+        "flag": "SV",
+        "isCrypto": false
+    },
+    "SZL":
+    {
+        "symbol": "SZL",
+        "name": "Swazi Lilangeni",
+        "flag": "SZ",
+        "isCrypto": false
+    },
+    "THB":
+    {
+        "symbol": "THB",
+        "name": "Thai Baht",
+        "flag": "TH",
+        "isCrypto": false
+    },
+    "TJS":
+    {
+        "symbol": "TJS",
+        "name": "Tajikistani Somoni",
+        "flag": "TJ",
+        "isCrypto": false
+    },
+    "TMT":
+    {
+        "symbol": "TMT",
+        "name": "Turkmenistani Manat",
+        "flag": "TM",
+        "isCrypto": false
+    },
+    "TND":
+    {
+        "symbol": "TND",
+        "name": "Tunisian Dinar",
+        "flag": "TN",
+        "isCrypto": false
+    },
+    "TOP":
+    {
+        "symbol": "TOP",
+        "name": "Tongan PaʻAnga",
+        "flag": "TO",
+        "isCrypto": false
+    },
+    "TRY":
+    {
+        "symbol": "TRY",
+        "name": "Turkish Lira",
+        "flag": "TR",
+        "isCrypto": false
+    },
+    "TTD":
+    {
+        "symbol": "TTD",
+        "name": "Trinidad And Tobago Dollar",
+        "flag": "TT",
+        "isCrypto": false
+    },
+    "TWD":
+    {
+        "symbol": "TWD",
+        "name": "New Taiwan Dollar",
+        "flag": "TW",
+        "isCrypto": false
+    },
+    "TZS":
+    {
+        "symbol": "TZS",
+        "name": "Tanzanian Shilling",
+        "flag": "TZ",
+        "isCrypto": false
+    },
+    "UAH":
+    {
+        "symbol": "UAH",
+        "name": "Ukrainian Hryvnia",
+        "flag": "UA",
+        "isCrypto": false
+    },
+    "UGX":
+    {
+        "symbol": "UGX",
+        "name": "Ugandan Shilling",
+        "flag": "UG",
+        "isCrypto": false
+    },
+    "USD":
+    {
+        "symbol": "USD",
+        "name": "United States Dollar",
+        "flag": "US",
+        "isCrypto": false
+    },
+    "UYU":
+    {
+        "symbol": "UYU",
+        "name": "Uruguayan Peso",
+        "flag": "UY",
+        "isCrypto": false
+    },
+    "UZS":
+    {
+        "symbol": "UZS",
+        "name": "Uzbekistan Som",
+        "flag": "UZ",
+        "isCrypto": false
+    },
+    "VES":
+    {
+        "symbol": "VES",
+        "name": "Venezuelan Bolivar Soberano",
+        "flag": "VE",
+        "isCrypto": false
+    },
+    "VND":
+    {
+        "symbol": "VND",
+        "name": "Vietnamese Dong",
+        "flag": "VN",
+        "isCrypto": false
+    },
+    "VRL":
+    {
+        "symbol": "VRL",
+        "name": "Vreneli 10F.",
+        "flag": "CH",
+        "isCrypto": false
+    },
+    "VRN":
+    {
+        "symbol": "VRN",
+        "name": "Vreneli 20F",
+        "flag": "CH",
+        "isCrypto": false
+    },
+    "XAG":
+    {
+        "symbol": "XAG",
+        "name": "Silver (One Troy Ounce)",
+        "flag": "XAG",
+        "isCrypto": false
+    },
+    "XAGK":
+    {
+        "symbol": "XAGK",
+        "name": "Silver (Kg)",
+        "flag": "XAG",
+        "isCrypto": false
+    },
+    "XAU":
+    {
+        "symbol": "XAU",
+        "name": "Gold (One Troy Ounce)",
+        "flag": "XAU",
+        "isCrypto": false
+    },
+    "XAUK":
+    {
+        "symbol": "XAUK",
+        "name": "Gold (Kg)",
+        "flag": "XAU",
+        "isCrypto": false
+    },
+    "XCD":
+    {
+        "symbol": "XCD",
+        "name": "East Caribbean Dollar",
+        "flag": "AI",
+        "isCrypto": false
+    },
+    "XOF":
+    {
+        "symbol": "XOF",
+        "name": "Cfa Franc Bceao",
+        "flag": "SN",
+        "isCrypto": false
+    },
+    "XPD":
+    {
+        "symbol": "XPD",
+        "name": "Palladium (One Troy Ounce)",
+        "flag": "XPD",
+        "isCrypto": false
+    },
+    "XPDK":
+    {
+        "symbol": "XPDK",
+        "name": "Palladium (Kg)",
+        "flag": "XPD",
+        "isCrypto": false
+    },
+    "XPF":
+    {
+        "symbol": "XPF",
+        "name": "Cfp Franc",
+        "flag": "PF",
+        "isCrypto": false
+    },
+    "XPT":
+    {
+        "symbol": "XPT",
+        "name": "Platinum (One Troy Ounce)",
+        "flag": "XPT",
+        "isCrypto": false
+    },
+    "XPTK":
+    {
+        "symbol": "XPTK",
+        "name": "Platinum (Kg)",
+        "flag": "XPT",
+        "isCrypto": false
+    },
+    "YER":
+    {
+        "symbol": "YER",
+        "name": "Yemeni Rial",
+        "flag": "YE",
+        "isCrypto": false
+    },
+    "ZAR":
+    {
+        "symbol": "ZAR",
+        "name": "South African Rand",
+        "flag": "ZA",
+        "isCrypto": false
+    },
+    "ZMW":
+    {
+        "symbol": "ZMW",
+        "name": "Zambian Kwacha",
+        "flag": "ZM",
+        "isCrypto": false
+    },
+    "ZWD":
+    {
+        "symbol": "ZWD",
+        "name": "Zimbabwe Dollar",
+        "flag": "ZW",
+        "isCrypto": false
+    },
+    "DASH":
+    {
+        "symbol": "DASH",
+        "name": "Dash",
+        "flag": "DASH",
+        "isCrypto": true
+    },
+    "BTC":
+    {
+        "symbol": "BTC",
+        "name": "Bitcoin",
+        "flag": "BTC",
+        "isCrypto": true
+    },
+    "DSH":
+    {
+        "symbol": "DSH",
+        "name": "Dash Coin",
+        "flag": "DASH",
+        "isCrypto": true
+    },
+    "LTC":
+    {
+        "symbol": "LTC",
+        "name": "Litecoin",
+        "flag": "LTC",
+        "isCrypto": true
+    },
+    "ETH":
+    {
+        "symbol": "ETH",
+        "name": "Ethereum",
+        "flag": "ETH",
+        "isCrypto": true
+    },
+    "BCH":
+    {
+        "symbol": "BCH",
+        "name": "Bitcoin Cash",
+        "flag": "BTC",
+        "isCrypto": true
+    },
+    "XRP":
+    {
+        "symbol": "XRP",
+        "name": "Xrp Cryptocurrency",
+        "flag": "XRP",
+        "isCrypto": true
+    },
+    "XLM":
+    {
+        "symbol": "XLM",
+        "name": "Stellar",
+        "flag": "XLM",
+        "isCrypto": true
+    },
+    "ADA":
+    {
+        "symbol": "ADA",
+        "name": "Cardano",
+        "flag": "ADA",
+        "isCrypto": true
+    },
+    "UKO":
+    {
+        "symbol": "UKO",
+        "name": "Brent Crude Oil",
+        "flag": "UKO",
+        "isCrypto": false
+    },
+    "AAVE":
+    {
+        "symbol": "AAVE",
+        "name": "Aave Token",
+        "flag": "AAVE",
+        "isCrypto": true
+    },
+    "UNI":
+    {
+        "symbol": "UNI",
+        "name": "Uniswap",
+        "flag": "UNI",
+        "isCrypto": true
+    },
+    "LUNA":
+    {
+        "symbol": "LUNA",
+        "name": "Terra",
+        "flag": "LUNA",
+        "isCrypto": true
+    },
+    "XMR":
+    {
+        "symbol": "XMR",
+        "name": "Monero",
+        "flag": "XMR",
+        "isCrypto": true
+    },
+    "XDR":
+    {
+        "symbol": "XDR",
+        "name": "Special Drawing Rights",
+        "flag": "XDR",
+        "isCrypto": false
+    },
+    "SOL":
+    {
+        "symbol": "SOL",
+        "name": "Solana",
+        "flag": "SOL",
+        "isCrypto": true
+    },
+    "DOGE":
+    {
+        "symbol": "DOGE",
+        "name": "Dogecoin",
+        "flag": "DOGE",
+        "isCrypto": true
+    },
+    "VET":
+    {
+        "symbol": "VET",
+        "name": "VeChain",
+        "flag": "VET",
+        "isCrypto": true
+    },
+    "DOT":
+    {
+        "symbol": "DOT",
+        "name": "Polkadot",
+        "flag": "DOT",
+        "isCrypto": true
+    },
+    "USDC":
+    {
+        "symbol": "USDC",
+        "name": "USD Coin",
+        "flag": "USDC",
+        "isCrypto": true
+    },
+    "FIL":
+    {
+        "symbol": "FIL",
+        "name": "File Coin",
+        "flag": "FIL",
+        "isCrypto": true
+    },
+    "LINK":
+    {
+        "symbol": "LINK",
+        "name": "Chainlink",
+        "flag": "LINK",
+        "isCrypto": true
+    },
+    "MATIC":
+    {
+        "symbol": "MATIC",
+        "name": "Polygon",
+        "flag": "MATIC",
+        "isCrypto": true
+    },
+    "THETA":
+    {
+        "symbol": "THETA",
+        "name": "Theta",
+        "flag": "THETA",
+        "isCrypto": true
+    },
+    "BNB":
+    {
+        "symbol": "BNB",
+        "name": "Binance Coin",
+        "flag": "BNB",
+        "isCrypto": true
+    }
+}
--- a/Simoleon/Resources/CurrencyDetails.json	Wed Aug 25 11:00:21 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,982 +0,0 @@
-{
-    "AED": {
-        "name": "United Arab Emirates Dirham",
-        "flag": "AE",
-        "isCrypto": false
-    },
-    "AFN": {
-        "name": "Afghan Afghani",
-        "flag": "AF",
-        "isCrypto": false
-    },
-    "ALL": {
-        "name": "Albanian Lek",
-        "flag": "AL",
-        "isCrypto": false
-    },
-    "AMD": {
-        "name": "Armenian Dram",
-        "flag": "AM",
-        "isCrypto": false
-    },
-    "ANG": {
-        "name": "Netherlands Antillean Guilder",
-        "flag": "CW",
-        "isCrypto": false
-    },
-    "AOA": {
-        "name": "Angolan Kwanza",
-        "flag": "AO",
-        "isCrypto": false
-    },
-    "ARE": {
-        "name": "Areg",
-        "flag": "AR",
-        "isCrypto": false
-    },
-    "ARS": {
-        "name": "Argentine Peso",
-        "flag": "AR",
-        "isCrypto": false
-    },
-    "AUD": {
-        "name": "Australian Dollar",
-        "flag": "AU",
-        "isCrypto": false
-    },
-    "AUN": {
-        "name": "Australian Nugget",
-        "flag": "AU",
-        "isCrypto": false
-    },
-    "AWG": {
-        "name": "Aruban Florin",
-        "flag": "AW",
-        "isCrypto": false
-    },
-    "BAM": {
-        "name": "Bosnia And Herzegovina Convertible Mark",
-        "flag": "BA",
-        "isCrypto": false
-    },
-    "BBD": {
-        "name": "Barbados Dollar",
-        "flag": "BB",
-        "isCrypto": false
-    },
-    "BDT": {
-        "name": "Bangladeshi Taka",
-        "flag": "BD",
-        "isCrypto": false
-    },
-    "BGN": {
-        "name": "Bulgarian Lev",
-        "flag": "BG",
-        "isCrypto": false
-    },
-    "BHD": {
-        "name": "Bahraini Dinar",
-        "flag": "BH",
-        "isCrypto": false
-    },
-    "BIF": {
-        "name": "Burundian Franc",
-        "flag": "BI",
-        "isCrypto": false
-    },
-    "BMD": {
-        "name": "Bermudian Dollar",
-        "flag": "BM",
-        "isCrypto": false
-    },
-    "BND": {
-        "name": "Brunei Dollar",
-        "flag": "BN",
-        "isCrypto": false
-    },
-    "BOB": {
-        "name": "Boliviano",
-        "flag": "BO",
-        "isCrypto": false
-    },
-    "BRI": {
-        "name": "Britannia",
-        "flag": "BR",
-        "isCrypto": false
-    },
-    "BRL": {
-        "name": "Brazilian Real",
-        "flag": "BR",
-        "isCrypto": false
-    },
-    "BSD": {
-        "name": "Bahamian Dollar",
-        "flag": "BS",
-        "isCrypto": false
-    },
-    "BTN": {
-        "name": "Bhutanese Ngultrum",
-        "flag": "BT",
-        "isCrypto": false
-    },
-    "BWP": {
-        "name": "Botswana Pula",
-        "flag": "BW",
-        "isCrypto": false
-    },
-    "BYN": {
-        "name": "Belarusian Ruble",
-        "flag": "BY",
-        "isCrypto": false
-    },
-    "BZD": {
-        "name": "Belize Dollar",
-        "flag": "BZ",
-        "isCrypto": false
-    },
-    "CAD": {
-        "name": "Canadian Dollar",
-        "flag": "CA",
-        "isCrypto": false
-    },
-    "CDF": {
-        "name": "Congolese Franc",
-        "flag": "CD",
-        "isCrypto": false
-    },
-    "CHF": {
-        "name": "Swiss Franc",
-        "flag": "CH",
-        "isCrypto": false
-    },
-    "CLF": {
-        "name": "Chilean Unit Of Account",
-        "flag": "CL",
-        "isCrypto": false
-    },
-    "CLP": {
-        "name": "Chilean Peso",
-        "flag": "CL",
-        "isCrypto": false
-    },
-    "CNH": {
-        "name": "Chinese Yuan",
-        "flag": "CN",
-        "isCrypto": false
-    },
-    "CNY": {
-        "name": "Chinese Yuan",
-        "flag": "CN",
-        "isCrypto": false
-    },
-    "COP": {
-        "name": "Colombian Peso",
-        "flag": "CO",
-        "isCrypto": false
-    },
-    "CRC": {
-        "name": "Costa Rican Colon",
-        "flag": "CR",
-        "isCrypto": false
-    },
-    "CUP": {
-        "name": "Cuban Peso",
-        "flag": "CU",
-        "isCrypto": false
-    },
-    "CVE": {
-        "name": "Cape Verde Escudo",
-        "flag": "CV",
-        "isCrypto": false
-    },
-    "CYP": {
-        "name": "Cypriot Pound",
-        "flag": "CY",
-        "isCrypto": false
-    },
-    "CZK": {
-        "name": "Czech Koruna",
-        "flag": "CZ",
-        "isCrypto": false
-    },
-    "DJF": {
-        "name": "Djiboutian Franc",
-        "flag": "DJ",
-        "isCrypto": false
-    },
-    "DKK": {
-        "name": "Danish Krone",
-        "flag": "DK",
-        "isCrypto": false
-    },
-    "DOE": {
-        "name": "Double Eagle",
-        "flag": "DO",
-        "isCrypto": false
-    },
-    "DOP": {
-        "name": "Dominican Peso",
-        "flag": "DO",
-        "isCrypto": false
-    },
-    "DZD": {
-        "name": "Algerian Dinar",
-        "flag": "DZ",
-        "isCrypto": false
-    },
-    "EGP": {
-        "name": "Egyptian Pound",
-        "flag": "EG",
-        "isCrypto": false
-    },
-    "ETB": {
-        "name": "Ethiopian Birr",
-        "flag": "ET",
-        "isCrypto": false
-    },
-    "EUR": {
-        "name": "Euro",
-        "flag": "EU",
-        "isCrypto": false
-    },
-    "FJD": {
-        "name": "Fiji Dollar",
-        "flag": "FJ",
-        "isCrypto": false
-    },
-    "FRN": {
-        "name": "French Napoleon",
-        "flag": "FR",
-        "isCrypto": false
-    },
-    "GBP": {
-        "name": "Pound Sterling",
-        "flag": "GB",
-        "isCrypto": false
-    },
-    "GEL": {
-        "name": "Georgian Lari",
-        "flag": "GE",
-        "isCrypto": false
-    },
-    "GHS": {
-        "name": "Ghanaian Cedi",
-        "flag": "GH",
-        "isCrypto": false
-    },
-    "GMD": {
-        "name": "Gambian Dalasi",
-        "flag": "GM",
-        "isCrypto": false
-    },
-    "GNF": {
-        "name": "Guinean Franc",
-        "flag": "GN",
-        "isCrypto": false
-    },
-    "GTQ": {
-        "name": "Guatemalan Quetzal",
-        "flag": "GT",
-        "isCrypto": false
-    },
-    "GYD": {
-        "name": "Guyanese Dollar",
-        "flag": "GY",
-        "isCrypto": false
-    },
-    "HKD": {
-        "name": "Hong Kong Dollar",
-        "flag": "HK",
-        "isCrypto": false
-    },
-    "HNL": {
-        "name": "Honduran Lempira",
-        "flag": "HN",
-        "isCrypto": false
-    },
-    "HRK": {
-        "name": "Croatian Kuna",
-        "flag": "HR",
-        "isCrypto": false
-    },
-    "HTG": {
-        "name": "Haitian Gourde",
-        "flag": "HT",
-        "isCrypto": false
-    },
-    "HUF": {
-        "name": "Hungarian Forint",
-        "flag": "HU",
-        "isCrypto": false
-    },
-    "IDR": {
-        "name": "Indonesian Rupiah",
-        "flag": "ID",
-        "isCrypto": false
-    },
-    "ILS": {
-        "name": "Israeli New Shekel",
-        "flag": "IL",
-        "isCrypto": false
-    },
-    "INR": {
-        "name": "Indian Rupee",
-        "flag": "IN",
-        "isCrypto": false
-    },
-    "IQD": {
-        "name": "Iraqi Dinar",
-        "flag": "IQ",
-        "isCrypto": false
-    },
-    "IRR": {
-        "name": "Iranian Rial",
-        "flag": "IR",
-        "isCrypto": false
-    },
-    "ISK": {
-        "name": "Icelandic Króna",
-        "flag": "IS",
-        "isCrypto": false
-    },
-    "JMD": {
-        "name": "Jamaican Dollar",
-        "flag": "JM",
-        "isCrypto": false
-    },
-    "JOD": {
-        "name": "Jordanian Dinar",
-        "flag": "JO",
-        "isCrypto": false
-    },
-    "JPY": {
-        "name": "Japanese Yen",
-        "flag": "JP",
-        "isCrypto": false
-    },
-    "KES": {
-        "name": "Kenyan Shilling",
-        "flag": "KE",
-        "isCrypto": false
-    },
-    "KHR": {
-        "name": "Cambodian Riel",
-        "flag": "KH",
-        "isCrypto": false
-    },
-    "KMF": {
-        "name": "Comoro Franc",
-        "flag": "KM",
-        "isCrypto": false
-    },
-    "KRU": {
-        "name": "South African Krugerrand",
-        "flag": "KR",
-        "isCrypto": false
-    },
-    "KRW": {
-        "name": "South Korean Won",
-        "flag": "KR",
-        "isCrypto": false
-    },
-    "KWD": {
-        "name": "Kuwaiti Dinar",
-        "flag": "KW",
-        "isCrypto": false
-    },
-    "KYD": {
-        "name": "Cayman Islands Dollar",
-        "flag": "KY",
-        "isCrypto": false
-    },
-    "KZT": {
-        "name": "Kazakhstani Tenge",
-        "flag": "KZ",
-        "isCrypto": false
-    },
-    "LAK": {
-        "name": "Lao Kip",
-        "flag": "LA",
-        "isCrypto": false
-    },
-    "LBP": {
-        "name": "Lebanese Pound",
-        "flag": "LB",
-        "isCrypto": false
-    },
-    "LFX": {
-        "name": "Khazanah Sukuk",
-        "flag": "MY",
-        "isCrypto": false
-    },
-    "LKR": {
-        "name": "Sri Lankan Rupee",
-        "flag": "LK",
-        "isCrypto": false
-    },
-    "LRD": {
-        "name": "Liberian Dollar",
-        "flag": "LR",
-        "isCrypto": false
-    },
-    "LSL": {
-        "name": "Lesotho Loti",
-        "flag": "LS",
-        "isCrypto": false
-    },
-    "LTL": {
-        "name": "Lithuanian Litas",
-        "flag": "LT",
-        "isCrypto": false
-    },
-    "LYD": {
-        "name": "Libyan Dinar",
-        "flag": "LY",
-        "isCrypto": false
-    },
-    "M5P": {
-        "name": "Mexican 50 Peso",
-        "flag": "MX",
-        "isCrypto": false
-    },
-    "MAD": {
-        "name": "Moroccan Dirham",
-        "flag": "MA",
-        "isCrypto": false
-    },
-    "MAL": {
-        "name": "Maple Leaf",
-        "flag": "MA",
-        "isCrypto": false
-    },
-    "MDL": {
-        "name": "Moldovan Leu",
-        "flag": "MD",
-        "isCrypto": false
-    },
-    "MGA": {
-        "name": "Malagasy Ariary",
-        "flag": "MG",
-        "isCrypto": false
-    },
-    "MKD": {
-        "name": "Macedonian Denar",
-        "flag": "MK",
-        "isCrypto": false
-    },
-    "MMK": {
-        "name": "Myanma Kyat",
-        "flag": "MM",
-        "isCrypto": false
-    },
-    "MOP": {
-        "name": "Macanese Pataca",
-        "flag": "MO",
-        "isCrypto": false
-    },
-    "MRU": {
-        "name": "Mauritanian Ouguiya",
-        "flag": "MR",
-        "isCrypto": false
-    },
-    "MTL": {
-        "name": "Maltese Lira",
-        "flag": "MT",
-        "isCrypto": false
-    },
-    "MUR": {
-        "name": "Mauritian Rupee",
-        "flag": "MU",
-        "isCrypto": false
-    },
-    "MVR": {
-        "name": "Maldivian Rufiyaa",
-        "flag": "MV",
-        "isCrypto": false
-    },
-    "MWK": {
-        "name": "Malawian Kwacha",
-        "flag": "MW",
-        "isCrypto": false
-    },
-    "MXN": {
-        "name": "Mexican Peso",
-        "flag": "MX",
-        "isCrypto": false
-    },
-    "MYR": {
-        "name": "Malaysian Ringgit",
-        "flag": "MY",
-        "isCrypto": false
-    },
-    "MZN": {
-        "name": "Mozambican Metical",
-        "flag": "MZ",
-        "isCrypto": false
-    },
-    "NAD": {
-        "name": "Namibian Dollar",
-        "flag": "NA",
-        "isCrypto": false
-    },
-    "NBL": {
-        "name": "Isle Of Man Noble",
-        "flag": "IM",
-        "isCrypto": false
-    },
-    "NGN": {
-        "name": "Nigerian Naira",
-        "flag": "NG",
-        "isCrypto": false
-    },
-    "NIO": {
-        "name": "Nicaraguan Córdoba",
-        "flag": "NI",
-        "isCrypto": false
-    },
-    "NOK": {
-        "name": "Norwegian Krone",
-        "flag": "NO",
-        "isCrypto": false
-    },
-    "NPR": {
-        "name": "Nepalese Rupee",
-        "flag": "NP",
-        "isCrypto": false
-    },
-    "NSO": {
-        "name": "New Sovereign",
-        "flag": "GB",
-        "isCrypto": false
-    },
-    "NZD": {
-        "name": "New Zealand Dollar",
-        "flag": "NZ",
-        "isCrypto": false
-    },
-    "OMR": {
-        "name": "Omani Rial",
-        "flag": "OM",
-        "isCrypto": false
-    },
-    "OSO": {
-        "name": "Old Sovereign",
-        "flag": "GB",
-        "isCrypto": false
-    },
-    "PAB": {
-        "name": "Panamanian Balboa",
-        "flag": "PA",
-        "isCrypto": false
-    },
-    "PEN": {
-        "name": "Peruvian Nuevo Sol",
-        "flag": "PE",
-        "isCrypto": false
-    },
-    "PGK": {
-        "name": "Papua New Guinean Kina",
-        "flag": "PG",
-        "isCrypto": false
-    },
-    "PHP": {
-        "name": "Philippine Peso",
-        "flag": "PH",
-        "isCrypto": false
-    },
-    "PKR": {
-        "name": "Pakistani Rupee",
-        "flag": "PK",
-        "isCrypto": false
-    },
-    "PLN": {
-        "name": "Polish Zloty",
-        "flag": "PL",
-        "isCrypto": false
-    },
-    "PYG": {
-        "name": "Paraguayan Guaraní",
-        "flag": "PY",
-        "isCrypto": false
-    },
-    "QAR": {
-        "name": "Qatari Riyal",
-        "flag": "QA",
-        "isCrypto": false
-    },
-    "RON": {
-        "name": "Romanian New Leu",
-        "flag": "RO",
-        "isCrypto": false
-    },
-    "RSD": {
-        "name": "Serbian Dinar",
-        "flag": "RS",
-        "isCrypto": false
-    },
-    "RUB": {
-        "name": "Russian Rouble",
-        "flag": "RU",
-        "isCrypto": false
-    },
-    "RWF": {
-        "name": "Rwandan Franc",
-        "flag": "RW",
-        "isCrypto": false
-    },
-    "SAR": {
-        "name": "Saudi Riyal",
-        "flag": "SA",
-        "isCrypto": false
-    },
-    "SBD": {
-        "name": "Solomon Islands Dollar",
-        "flag": "SB",
-        "isCrypto": false
-    },
-    "SCR": {
-        "name": "Seychelles Rupee",
-        "flag": "SC",
-        "isCrypto": false
-    },
-    "SDG": {
-        "name": "Sudanese Pound",
-        "flag": "SD",
-        "isCrypto": false
-    },
-    "SEK": {
-        "name": "Swedish Krona",
-        "flag": "SE",
-        "isCrypto": false
-    },
-    "SGD": {
-        "name": "Singapore Dollar",
-        "flag": "SG",
-        "isCrypto": false
-    },
-    "SHP": {
-        "name": "Saint Helena Pound",
-        "flag": "SH",
-        "isCrypto": false
-    },
-    "SLL": {
-        "name": "Sierra Leonean Leone",
-        "flag": "SL",
-        "isCrypto": false
-    },
-    "SOS": {
-        "name": "Somali Shilling",
-        "flag": "SO",
-        "isCrypto": false
-    },
-    "SRD": {
-        "name": "Surinamese Dollar",
-        "flag": "SR",
-        "isCrypto": false
-    },
-    "STN": {
-        "name": "Sao Tomean Dobra",
-        "flag": "ST",
-        "isCrypto": false
-    },
-    "SVC": {
-        "name": "Salvadoran Colón",
-        "flag": "SV",
-        "isCrypto": false
-    },
-    "SZL": {
-        "name": "Swazi Lilangeni",
-        "flag": "SZ",
-        "isCrypto": false
-    },
-    "THB": {
-        "name": "Thai Baht",
-        "flag": "TH",
-        "isCrypto": false
-    },
-    "TJS": {
-        "name": "Tajikistani Somoni",
-        "flag": "TJ",
-        "isCrypto": false
-    },
-    "TMT": {
-        "name": "Turkmenistani Manat",
-        "flag": "TM",
-        "isCrypto": false
-    },
-    "TND": {
-        "name": "Tunisian Dinar",
-        "flag": "TN",
-        "isCrypto": false
-    },
-    "TOP": {
-        "name": "Tongan PaʻAnga",
-        "flag": "TO",
-        "isCrypto": false
-    },
-    "TRY": {
-        "name": "Turkish Lira",
-        "flag": "TR",
-        "isCrypto": false
-    },
-    "TTD": {
-        "name": "Trinidad And Tobago Dollar",
-        "flag": "TT",
-        "isCrypto": false
-    },
-    "TWD": {
-        "name": "New Taiwan Dollar",
-        "flag": "TW",
-        "isCrypto": false
-    },
-    "TZS": {
-        "name": "Tanzanian Shilling",
-        "flag": "TZ",
-        "isCrypto": false
-    },
-    "UAH": {
-        "name": "Ukrainian Hryvnia",
-        "flag": "UA",
-        "isCrypto": false
-    },
-    "UGX": {
-        "name": "Ugandan Shilling",
-        "flag": "UG",
-        "isCrypto": false
-    },
-    "USD": {
-        "name": "United States Dollar",
-        "flag": "US",
-        "isCrypto": false
-    },
-    "UYU": {
-        "name": "Uruguayan Peso",
-        "flag": "UY",
-        "isCrypto": false
-    },
-    "UZS": {
-        "name": "Uzbekistan Som",
-        "flag": "UZ",
-        "isCrypto": false
-    },
-    "VES": {
-        "name": "Venezuelan Bolivar Soberano",
-        "flag": "VE",
-        "isCrypto": false
-    },
-    "VND": {
-        "name": "Vietnamese Dong",
-        "flag": "VN",
-        "isCrypto": false
-    },
-    "VRL": {
-        "name": "Vreneli 10F.",
-        "flag": "CH",
-        "isCrypto": false
-    },
-    "VRN": {
-        "name": "Vreneli 20F",
-        "flag": "CH",
-        "isCrypto": false
-    },
-    "XAG": {
-        "name": "Silver (One Troy Ounce)",
-        "flag": "XAG",
-        "isCrypto": false
-    },
-    "XAGK": {
-        "name": "Silver (Kg)",
-        "flag": "XAG",
-        "isCrypto": false
-    },
-    "XAU": {
-        "name": "Gold (One Troy Ounce)",
-        "flag": "XAU",
-        "isCrypto": false
-    },
-    "XAUK": {
-        "name": "Gold (Kg)",
-        "flag": "XAU",
-        "isCrypto": false
-    },
-    "XCD": {
-        "name": "East Caribbean Dollar",
-        "flag": "AI",
-        "isCrypto": false
-    },
-    "XOF": {
-        "name": "Cfa Franc Bceao",
-        "flag": "SN",
-        "isCrypto": false
-    },
-    "XPD": {
-        "name": "Palladium (One Troy Ounce)",
-        "flag": "XPD",
-        "isCrypto": false
-    },
-    "XPDK": {
-        "name": "Palladium (Kg)",
-        "flag": "XPD",
-        "isCrypto": false
-    },
-    "XPF": {
-        "name": "Cfp Franc",
-        "flag": "PF",
-        "isCrypto": false
-    },
-    "XPT": {
-        "name": "Platinum (One Troy Ounce)",
-        "flag": "XPT",
-        "isCrypto": false
-    },
-    "XPTK": {
-        "name": "Platinum (Kg)",
-        "flag": "XPT",
-        "isCrypto": false
-    },
-    "YER": {
-        "name": "Yemeni Rial",
-        "flag": "YE",
-        "isCrypto": false
-    },
-    "ZAR": {
-        "name": "South African Rand",
-        "flag": "ZA",
-        "isCrypto": false
-    },
-    "ZMW": {
-        "name": "Zambian Kwacha",
-        "flag": "ZM",
-        "isCrypto": false
-    },
-    "ZWD": {
-        "name": "Zimbabwe Dollar",
-        "flag": "ZW",
-        "isCrypto": false
-    },
-    "DASH": {
-        "name": "Dash",
-        "flag": "DASH",
-        "isCrypto": true
-    },
-    "BTC": {
-        "name": "Bitcoin",
-        "flag": "BTC",
-        "isCrypto": true
-    },
-    "DSH": {
-        "name": "Dash Coin",
-        "flag": "DASH",
-        "isCrypto": true
-    },
-    "LTC": {
-        "name": "Litecoin",
-        "flag": "LTC",
-        "isCrypto": true
-    },
-    "ETH": {
-        "name": "Ethereum",
-        "flag": "ETH",
-        "isCrypto": true
-    },
-    "BCH": {
-        "name": "Bitcoin Cash",
-        "flag": "BTC",
-        "isCrypto": true
-    },
-    "XRP": {
-        "name": "Xrp Cryptocurrency",
-        "flag": "XRP",
-        "isCrypto": true
-    },
-    "XLM": {
-        "name": "Stellar",
-        "flag": "XLM",
-        "isCrypto": true
-    },
-    "ADA": {
-        "name": "Cardano",
-        "flag": "ADA",
-        "isCrypto": true
-    },
-    "UKO": {
-        "name": "Brent Crude Oil",
-        "flag": "UKO",
-        "isCrypto": false
-    },
-    "AAVE": {
-        "name": "Aave Token",
-        "flag": "AAVE",
-        "isCrypto": true
-    },
-    "UNI": {
-        "name": "Uniswap",
-        "flag": "UNI",
-        "isCrypto": true
-    },
-    "LUNA": {
-        "name": "Terra",
-        "flag": "LUNA",
-        "isCrypto": true
-    },
-    "XMR": {
-        "name": "Monero",
-        "flag": "XMR",
-        "isCrypto": true
-    },
-    "XDR": {
-        "name": "Special Drawing Rights",
-        "flag": "XDR",
-        "isCrypto": false
-    },
-    "SOL": {
-        "name": "Solana",
-        "flag": "SOL",
-        "isCrypto": true
-    },
-    "DOGE": {
-        "name": "Dogecoin",
-        "flag": "DOGE",
-        "isCrypto": true
-    },
-    "VET": {
-        "name": "VeChain",
-        "flag": "VET",
-        "isCrypto": true
-    },
-    "DOT": {
-        "name": "Polkadot",
-        "flag": "DOT",
-        "isCrypto": true
-    },
-    "USDC": {
-        "name": "USD Coin",
-        "flag": "USDC",
-        "isCrypto": true
-    },
-    "FIL": {
-        "name": "File Coin",
-        "flag": "FIL",
-        "isCrypto": true
-    },
-    "LINK": {
-        "name": "Chainlink",
-        "flag": "LINK",
-        "isCrypto": true
-    },
-    "MATIC": {
-        "name": "Polygon",
-        "flag": "MATIC",
-        "isCrypto": true
-    },
-    "THETA": {
-        "name": "Theta",
-        "flag": "THETA",
-        "isCrypto": true
-    },
-    "BNB": {
-        "name": "Binance Coin",
-        "flag": "BNB",
-        "isCrypto": true
-    }
-}
--- a/Simoleon/Settings.swift	Wed Aug 25 11:00:21 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,176 +0,0 @@
-//
-//  Settings.swift
-//  Simoleon
-//
-//  Created by Dennis Concepción Martín on 19/07/2021.
-//
-
-import SwiftUI
-import Purchases
-
-struct Settings: View {
-    @Environment(\.managedObjectContext) private var viewContext
-    @FetchRequest(sortDescriptors: []) private var defaultCurrency: FetchedResults<DefaultCurrency>
-    
-    @State private var selectedDefaultCurrency = ""
-    @State private var showingSubscriptionPaywall = false
-    @State private var entitlementIsActive = false
-    @State private var alertTitle = ""
-    @State private var alertMessage = ""
-    @State private var showingAlert = false
-    @State private var searchCurrency = ""
-    
-    let fileController = FileController()
-    
-    /*
-     If searched currency string is empty:
-     * Show all currencies
-     else:
-     * Show filtered list of currencies containing searched currency string
-     */
-    var searchResults: [String] {
-        let currencyPairsSupported: [String] = try! fileController.read(json: "CurrencyPairsSupported.json")
-        if searchCurrency.isEmpty {
-            return currencyPairsSupported.sorted()
-        } else {
-            return currencyPairsSupported.filter { $0.contains(searchCurrency.uppercased()) }
-        }
-    }
-    
-    var body: some View {
-        List {
-            Section(header: Text("Preferences")) {
-                if entitlementIsActive {
-                    Picker("Default currency", selection: $selectedDefaultCurrency) {
-                        SearchBar(placeholder: "Search...", text: $searchCurrency)
-                            .padding(5)
-                        
-                        ForEach(searchResults, id: \.self) { currencyPairsSupported in
-                            Text(currencyPairsSupported)
-                                .tag(currencyPairsSupported)
-                        }
-                    }
-                } else {
-                    LockedCurrencyPicker()
-                        .contentShape(Rectangle())
-                        .onTapGesture { showingSubscriptionPaywall = true }
-                }
-            }
-            
-            Section(header: Text("Stay in touch")) {
-                Link(destination: URL(string: "https://itunes.apple.com/app/id1576390953?action=write-review")!) {
-                    HStack {
-                        Image(systemName: "heart.fill")
-                            .foregroundColor(Color(.systemRed))
-                            .imageScale(.large)
-
-                        Text("Rate Simoleon")
-                    }
-                }
-                
-                Link(destination: URL(string: "https://twitter.com/dennisconcep")!) {
-                    HStack {
-                        Image("TwitterLogo")
-                            .resizable()
-                            .frame(width: 30, height: 30)
-                        
-                        Text("Developer's Twitter")
-                    }
-                }
-                
-                Link(destination: URL(string: "https://dennistech.io/contact")!) {
-                    HStack {
-                        Image(systemName: "envelope.fill")
-                            .foregroundColor(Color(.systemIndigo))
-                            .imageScale(.large)
-                        
-                        Text("Contact")
-                    }
-                }
-            }
-            
-            Section(header: Text("About")) {
-                Link(destination: URL(string: "https://dennistech.io")!) {
-                    Text("Website")
-                }
-                
-                Link(destination: URL(string: "https://dennistech.io/simoleon-privacy-policy")!) {
-                    Text("Privacy Policy")
-                }
-                
-                Link(destination: URL(string: "https://dennistech.io/simoleon-terms-of-use")!) {
-                    Text("Terms of Use")
-                }
-            }
-        }
-        .alert(isPresented: $showingAlert) {
-            Alert(title: Text(alertTitle), message: Text(alertMessage), dismissButton: .default(Text("Ok")))
-        }
-        .onAppear {
-            checkEntitlement()
-            /*
-             if selectedDefaultCurrency is empty:
-             * View is appearing for the first time
-             * Set initial default curency for picker
-             else:
-             * View is appearing after user selected another default currency
-             * Save it to core data
-             */
-//            if selectedDefaultCurrency == "" {
-//                selectedDefaultCurrency = defaultCurrency.first?.pair ?? "USD/GBP"
-//            } else {
-//                setCoreData()
-//            }
-        }
-        .listStyle(InsetGroupedListStyle())
-        .navigationTitle("Settings")
-        .sheet(isPresented: $showingSubscriptionPaywall, onDismiss: checkEntitlement) {
-            SubscriptionPaywall(showingSubscriptionPaywall: $showingSubscriptionPaywall)
-        }
-        .if(UIDevice.current.userInterfaceIdiom == .phone) { content in
-            NavigationView { content }
-        }
-    }
-     
-    // Save default currency to core data
-//    private func setCoreData() {
-//        if defaultCurrency.isEmpty {  // If it's empty -> add record
-//            let defaultCurrency = DefaultCurrency(context: viewContext)
-//            defaultCurrency.pair = selectedDefaultCurrency
-//            
-//            do {
-//                try viewContext.save()
-//            } catch {
-//                print(error.localizedDescription)
-//            }
-//        } else {  // If not, update record
-//            defaultCurrency.first?.pair = selectedDefaultCurrency
-//            try? viewContext.save()
-//        }
-//    }
-    
-    // Check if user subscription is active
-    private func checkEntitlement() {
-        #if SCREENSHOTS
-        entitlementIsActive = true
-        #else
-        Purchases.shared.purchaserInfo { (purchaserInfo, error) in
-            if purchaserInfo?.entitlements["all"]?.isActive == true {
-                entitlementIsActive = true
-            }
-            
-            if let error = error as NSError? {
-                alertTitle = error.localizedDescription
-                alertMessage = error.localizedFailureReason ?? ""
-                showingAlert = true
-            }
-        }
-        #endif
-    }
-}
-
-struct Settings_Previews: PreviewProvider {
-    static var previews: some View {
-        Settings()
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/SettingsView.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -0,0 +1,174 @@
+////
+////  SettingsView.swift
+////  Simoleon
+////
+////  Created by Dennis Concepción Martín on 19/07/2021.
+////
+//
+//import SwiftUI
+//import Purchases
+//
+//struct SettingsView: View {
+//    @Environment(\.managedObjectContext) private var viewContext
+//    @FetchRequest(sortDescriptors: []) private var defaultCurrency: FetchedResults<DefaultCurrency>
+//    @State private var selectedDefaultCurrency = ""
+//    @State private var showingSubscriptionPaywall = false
+//    @State private var entitlementIsActive = false
+//    @State private var alertTitle = ""
+//    @State private var alertMessage = ""
+//    @State private var showingAlert = false
+//    @State private var searchCurrency = ""
+//    let file = File()
+//    
+//    /*
+//     If searched currency string is empty:
+//     * Show all currencies
+//     else:
+//     * Show filtered list of currencies containing searched currency string
+//     */
+//    var searchResults: [String] {
+//        let currencyPairsSupported: [String] = try! file.read(json: "CurrencyPairsSupported.json")
+//        if searchCurrency.isEmpty {
+//            return currencyPairsSupported.sorted()
+//        } else {
+//            return currencyPairsSupported.filter { $0.contains(searchCurrency.uppercased()) }
+//        }
+//    }
+//    
+//    var body: some View {
+//        List {
+//            Section(header: Text("Preferences")) {
+//                if entitlementIsActive {
+//                    Picker("Default currency", selection: $selectedDefaultCurrency) {
+//                        SearchBar(placeholder: "Search...", text: $searchCurrency)
+//                            .padding(5)
+//                        
+//                        ForEach(searchResults, id: \.self) { currencyPairsSupported in
+//                            Text(currencyPairsSupported)
+//                                .tag(currencyPairsSupported)
+//                        }
+//                    }
+//                } else {
+//                    LockedCurrencyPicker()
+//                        .contentShape(Rectangle())
+//                        .onTapGesture { showingSubscriptionPaywall = true }
+//                }
+//            }
+//            
+//            Section(header: Text("Stay in touch")) {
+//                Link(destination: URL(string: "https://itunes.apple.com/app/id1576390953?action=write-review")!) {
+//                    HStack {
+//                        Image(systemName: "heart.fill")
+//                            .foregroundColor(Color(.systemRed))
+//                            .imageScale(.large)
+//
+//                        Text("Rate Simoleon")
+//                    }
+//                }
+//                
+//                Link(destination: URL(string: "https://twitter.com/dennisconcep")!) {
+//                    HStack {
+//                        Image("TwitterLogo")
+//                            .resizable()
+//                            .frame(width: 30, height: 30)
+//                        
+//                        Text("Developer's Twitter")
+//                    }
+//                }
+//                
+//                Link(destination: URL(string: "https://dennistech.io/contact")!) {
+//                    HStack {
+//                        Image(systemName: "envelope.fill")
+//                            .foregroundColor(Color(.systemIndigo))
+//                            .imageScale(.large)
+//                        
+//                        Text("Contact")
+//                    }
+//                }
+//            }
+//            
+//            Section(header: Text("About")) {
+//                Link(destination: URL(string: "https://dennistech.io")!) {
+//                    Text("Website")
+//                }
+//                
+//                Link(destination: URL(string: "https://dennistech.io/simoleon-privacy-policy")!) {
+//                    Text("Privacy Policy")
+//                }
+//                
+//                Link(destination: URL(string: "https://dennistech.io/simoleon-terms-of-use")!) {
+//                    Text("Terms of Use")
+//                }
+//            }
+//        }
+//        .alert(isPresented: $showingAlert) {
+//            Alert(title: Text(alertTitle), message: Text(alertMessage), dismissButton: .default(Text("Ok")))
+//        }
+//        .onAppear {
+//            checkEntitlement()
+//            /*
+//             if selectedDefaultCurrency is empty:
+//             * View is appearing for the first time
+//             * Set initial default curency for picker
+//             else:
+//             * View is appearing after user selected another default currency
+//             * Save it to core data
+//             */
+////            if selectedDefaultCurrency == "" {
+////                selectedDefaultCurrency = defaultCurrency.first?.pair ?? "USD/GBP"
+////            } else {
+////                setCoreData()
+////            }
+//        }
+//        .listStyle(InsetGroupedListStyle())
+//        .navigationTitle("Settings")
+//        .sheet(isPresented: $showingSubscriptionPaywall, onDismiss: checkEntitlement) {
+//            SubscriptionPaywall(showingSubscriptionPaywall: $showingSubscriptionPaywall)
+//        }
+//        .if(UIDevice.current.userInterfaceIdiom == .phone) { content in
+//            NavigationView { content }
+//        }
+//    }
+//     
+//    // Save default currency to core data
+////    private func setCoreData() {
+////        if defaultCurrency.isEmpty {  // If it's empty -> add record
+////            let defaultCurrency = DefaultCurrency(context: viewContext)
+////            defaultCurrency.pair = selectedDefaultCurrency
+////            
+////            do {
+////                try viewContext.save()
+////            } catch {
+////                print(error.localizedDescription)
+////            }
+////        } else {  // If not, update record
+////            defaultCurrency.first?.pair = selectedDefaultCurrency
+////            try? viewContext.save()
+////        }
+////    }
+//    
+//    // Check if user subscription is active
+//    private func checkEntitlement() {
+//        #if SCREENSHOTS
+//        entitlementIsActive = true
+//        #else
+//        Purchases.shared.purchaserInfo { (purchaserInfo, error) in
+//            if purchaserInfo?.entitlements["all"]?.isActive == true {
+//                entitlementIsActive = true
+//            }
+//            
+//            if let error = error as NSError? {
+//                alertTitle = error.localizedDescription
+//                alertMessage = error.localizedFailureReason ?? ""
+//                showingAlert = true
+//            }
+//        }
+//        #endif
+//    }
+//}
+//
+//struct SettingsView_Previews: PreviewProvider {
+//    static var previews: some View {
+//        SettingsView()
+//    }
+//}
--- a/Simoleon/Simoleon.xcdatamodeld/Simoleon.xcdatamodel/contents	Wed Aug 25 11:00:21 2021 +0100
+++ b/Simoleon/Simoleon.xcdatamodeld/Simoleon.xcdatamodel/contents	Sat Aug 28 11:15:25 2021 +0100
@@ -1,13 +1,14 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="18154" systemVersion="20G71" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier="">
+<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="18154" systemVersion="20G95" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier="">
     <entity name="DefaultCurrency" representedClassName="DefaultCurrency" syncable="YES">
-        <attribute name="pair" optional="YES" attributeType="String"/>
+        <attribute name="firstSymbol" optional="YES" attributeType="String"/>
+        <attribute name="secondSymbol" optional="YES" attributeType="String"/>
     </entity>
     <entity name="Favorite" representedClassName="Favorite" syncable="YES">
         <attribute name="currencyPair" optional="YES" attributeType="String"/>
     </entity>
     <elements>
-        <element name="DefaultCurrency" positionX="0" positionY="0" width="128" height="44"/>
+        <element name="DefaultCurrency" positionX="0" positionY="0" width="128" height="59"/>
         <element name="Favorite" positionX="0" positionY="0" width="128" height="44"/>
     </elements>
 </model>
\ No newline at end of file
--- a/Simoleon/SimoleonApp.swift	Wed Aug 25 11:00:21 2021 +0100
+++ b/Simoleon/SimoleonApp.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -12,10 +12,9 @@
 struct SimoleonApp: App {
     @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
     let persistenceController = PersistenceController.shared
-    let fileController = FileController()
     
     init() {
-        let apiKey = fileController.readConfigVariable(withKey: "PURCHASES_KEY")!
+        let apiKey = readConfigVariable(withKey: "PURCHASES_KEY")!
         Purchases.configure(withAPIKey: apiKey)
     }
     
--- a/Simoleon/SubscriptionPaywall.swift	Wed Aug 25 11:00:21 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,95 +0,0 @@
-//
-//  SubscriptionPaywall.swift
-//  Simoleon
-//
-//  Created by Dennis Concepción Martín on 22/07/2021.
-//
-
-import SwiftUI
-
-struct SubscriptionPaywall: View {
-    @Binding var showingSubscriptionPaywall: Bool
-    
-    var body: some View {
-        NavigationView {
-            ScrollView {
-                VStack(alignment: .leading, spacing: 20) {
-                    HStack {
-                        Spacer()
-                        VStack {
-                            Image("Subscription")
-                                .resizable()
-                                .aspectRatio(contentMode: .fit)
-                                .frame(width: 100, height: 100)
-                                .cornerRadius(25)
-                                .padding(.top)
-                            
-                            Text("Unlock All Access")
-                                .font(.title)
-                                .fontWeight(.semibold)
-                                .fixedSize(horizontal: false, vertical: true)
-                                .multilineTextAlignment(.center)
-                                .padding(.top)
-                        }
-                        
-                        Spacer()
-                    }
-                    
-                    Divider()
-                    
-                    SubscriptionFeature(
-                        symbol: "star.circle.fill",
-                        colour: Color(.systemYellow),
-                        title: "Favorite Forex Pairs",
-                        description: "Save any currency pair to access them quickly."
-                    )
-                    
-                    SubscriptionFeature(
-                        symbol: "flag.circle.fill",
-                        colour: Color(.systemRed),
-                        title: "Over 170 Currencies",
-                        description: "Access almost every currency of the world."
-                    )
-                    
-                    SubscriptionFeature(
-                        symbol: "icloud.circle.fill",
-                        colour: Color(.systemBlue),
-                        title: "Everything is Up-to-date",
-                        description: "Your settings and favorite currencies in all your devices."
-                    )
-                    
-                    SubscriptionFeature(
-                        symbol: "bitcoinsign.circle.fill",
-                        colour: Color(.systemOrange),
-                        title: "Cryptos and Commodities",
-                        description: "Convert currency between cryptos, gold, and silver."
-                    )
-                    
-                    Spacer()
-                    SubscribeButton(showingSubscriptionPaywall: $showingSubscriptionPaywall)
-                    HStack {
-                        Spacer()
-                        RestoreButton(showingSubscriptionPaywall: $showingSubscriptionPaywall)
-                        Spacer()
-                    }
-                }
-                .padding(.bottom)
-                .padding(.horizontal, 40)
-            }
-            .navigationBarTitleDisplayMode(.inline)
-            .toolbar {
-                ToolbarItem(placement: .cancellationAction) {
-                    Button(action: { showingSubscriptionPaywall = false }) {
-                        Text("Cancel")
-                    }
-                }
-            }
-        }
-    }
-}
-
-struct SubscriptionPaywall_Previews: PreviewProvider {
-    static var previews: some View {
-        SubscriptionPaywall(showingSubscriptionPaywall: .constant(true))
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/Tests/ChildListResets.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -0,0 +1,44 @@
+//
+//  ModalSheetSelection.swift
+//  Simoleon
+//
+//  Created by Dennis Concepción Martín on 26/8/21.
+//
+
+import SwiftUI
+
+struct ParentView: View {
+    @State var selection: Int = 1
+    @State private var showingList = false
+    
+    var body: some View {
+        VStack {
+            Button("Show list", action: {showingList = true})
+                .sheet(isPresented: $showingList) {
+                    ModalSheetSelection(selection: $selection)
+                }
+            
+            Text("My first var is: \(selection)")
+        }
+    }
+}
+
+struct ModalSheetSelection: View {
+    @Binding var selection: Int
+    
+    var body: some View {
+        NavigationView {
+            List {
+                SearchBar(placeholder: "", text: .constant(""))
+                ForEach((1..<100), id: \.self) { number in
+                    Button(action: {selection = number}) {
+                        Text("\(number)")
+                    }
+                }
+            }
+            .id(UUID())
+            .navigationTitle("Currencies")
+            .navigationBarTitleDisplayMode(.inline)
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/UI/ConversionBox.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -0,0 +1,83 @@
+////
+////  ConversionBox.swift
+////  Simoleon
+////
+////  Created by Dennis Concepción Martín on 18/07/2021.
+////
+//
+//import SwiftUI
+//
+//struct ConversionBox: View {
+//    var currencyDetails: CurrencyDetailsModel
+//    @State var currencyPair: CurrencyPairModel
+//    
+//    var body: some View {
+//        VStack(alignment: .leading) {
+//            Text("\(baseName) (\(currencyPair.baseSymbol))")
+//                .font(.callout)
+//                .fontWeight(.semibold)
+//                .padding(.top, 40)
+//            
+//            ZStack(alignment: .trailing) {
+//                TextField("Enter amount", text: $amount) { startedEditing in
+//                    if startedEditing {
+//                        withAnimation {
+//                            amountIsEditing = true
+//                        }
+//                    }
+//                }
+//                onCommit: {
+//                    withAnimation {
+//                        amountIsEditing = false
+//                    }
+//                }
+//                .keyboardType(.decimalPad)
+//                .font(Font.title.weight(.semibold))
+//                .lineLimit(1)
+//                .accessibilityIdentifier("ConversionTextField")
+//            }
+//            
+//            Divider()
+//
+//            let quoteName = currencyDetails[currencyPair.quoteSymbol]!.name
+//            Text("\(quoteName) (\(currencyPair.quoteSymbol))")
+//                .font(.callout)
+//                .fontWeight(.semibold)
+//                .padding(.top, 10)
+//            
+//            if showingConversion {
+//                Text("\(makeConversion(), specifier: "%.2f")")
+//                    .font(Font.title.weight(.semibold))
+//                    .lineLimit(1)
+//                    .padding(.top, 5)
+//            } else {
+//                ProgressView()
+//                    .padding(.top, 5)
+//            }
+//        }
+//        .onAppear(perform: request)
+//    }
+//    
+//    /*
+//     if the amount can be converted to Double:
+//     * Return amount
+//     else:
+//     * Return zero
+//     */
+//    func makeConversion() -> Double {
+//        if let amountToConvert = Double(amount) {
+//            return amountToConvert * price  // Conversion
+//        } else {
+//            return 0
+//        }
+//    }
+//}
+//
+//
+//struct ConversionBox_Previews: PreviewProvider {
+//    static var previews: some View {
+//        let fileController = File()
+//        let currencyDetails: [String: CurrencyDetailsModel] = try! fileController.read(json: "CurrencyDetails.json")
+//        ConversionBox(currencyPair: CurrencyPair(), currencyDetails: currencyDetails)
+//    }
+//}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/UI/CurrencyButton.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -0,0 +1,34 @@
+//
+//  CurrencyButton.swift
+//  Simoleon
+//
+//  Created by Dennis Concepción Martín on 24/8/21.
+//
+
+import SwiftUI
+
+struct CurrencyButton: View {
+    var selectedCurrency: String
+    let currencyDetails: [String: CurrencyModel] = try! read(json: "Currencies.json")
+    
+    var body: some View {
+        let currency = currencyDetails[selectedCurrency]!
+        RoundedRectangle(cornerRadius: 15)
+            .foregroundColor(Color(.secondarySystemBackground))
+            .frame(height: 60)
+            .overlay(
+                HStack {
+                    Flag(flag: currency.flag)
+                    Text(currency.symbol)
+                        .foregroundColor(.primary)
+                        .font(.headline)
+                }
+            )
+    }
+}
+
+struct CurrencyButton_Previews: PreviewProvider {
+    static var previews: some View {
+        CurrencyButton(selectedCurrency: "USD")
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/UI/CurrencyRow.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -0,0 +1,41 @@
+//
+//  CurrencyRow.swift
+//  Simoleon
+//
+//  Created by Dennis Concepción Martín on 15/07/2021.
+//
+
+import SwiftUI
+
+struct CurrencyRow: View {
+    var currency: CurrencyModel
+    
+    var body: some View {
+        HStack {
+            Flag(flag: currency.flag)
+            VStack(alignment: .leading) {
+                Text(currency.symbol)
+                    .font(.headline)
+                
+                Text(currency.name)
+                    .font(.subheadline)
+                    .lineLimit(1)
+            }
+            .padding(.horizontal)
+        }
+    }
+}
+
+struct CurrencyRow_Previews: PreviewProvider {
+    static var previews: some View {
+        CurrencyRow(
+            currency:
+                CurrencyModel(
+                    symbol: "USD",
+                    name: "United States Dollar",
+                    flag: "US",
+                    isCrypto: false
+                )
+        )
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/UI/FavoriteButton.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -0,0 +1,108 @@
+//
+//  FavoriteButton.swift
+//  Simoleon
+//
+//  Created by Dennis Concepción Martín on 19/07/2021.
+//
+
+import SwiftUI
+
+struct FavoriteButton: View {
+    @State var currencyPair: CurrencyPairModel
+    @Environment(\.managedObjectContext) private var viewContext
+    @FetchRequest(sortDescriptors: []) private var favorites: FetchedResults<Favorite>
+    @State private var starSymbol = "star"
+    
+    var body: some View {
+        Button(action: favoriteAction) {
+            RoundedRectangle(cornerRadius: 15)
+                .foregroundColor(Color(.secondarySystemBackground))
+                .frame(width: 60, height: 60)
+                .overlay(
+                    Image(systemName: generateStar())
+                        .font(.system(size: 28))
+                        .foregroundColor(Color(.systemYellow))
+                )
+        }
+        .accessibilityIdentifier("AddToFavorites")
+    }
+    
+    /*
+     If currency pair is favorite:
+     * Button action is to remove from favorites
+     else:
+     * Button action is to add to favorites
+     */
+    private func favoriteAction() {
+        let favoriteCurrencyPairs = favorites.map { $0.currencyPair }
+        let currencyPair = "(\(currencyPair.baseSymbol)/\(currencyPair.quoteSymbol)"
+        if favoriteCurrencyPairs.contains(currencyPair) {
+            removeFromFavorites()
+        } else {
+            addToFavorites()
+        }
+        
+        let haptics = Haptics()
+        haptics.simpleSuccess()
+    }
+    
+    /*
+     if currency pair is favorite:
+     * Return "star.fill" symbol
+     else:
+     * Return "star"
+     */
+    private func generateStar() -> String {
+        let favoriteCurrencyPairs = favorites.map { $0.currencyPair }
+        let currencyPair = "(\(currencyPair.baseSymbol)/\(currencyPair.quoteSymbol)"
+        if favoriteCurrencyPairs.contains(currencyPair) {
+            return "star.fill"
+        } else {
+            return "star"
+        }
+    }
+    
+    /*
+     * Get first favorite core data object that matches the specified currency pair
+     * Delete it
+     */
+    private func removeFromFavorites() {
+        let currencyPair = "(\(currencyPair.baseSymbol)/\(currencyPair.quoteSymbol)"
+        withAnimation {
+            let favoriteObject = favorites.first(where: { $0.currencyPair == currencyPair })
+            viewContext.delete(favoriteObject ?? Favorite())
+
+            do {
+                try viewContext.save()
+            } catch {
+                let nsError = error as NSError
+                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
+            }
+        }
+    }
+    
+    /*
+     * Create a favorite core data object
+     * Save it
+     */
+    private func addToFavorites() {
+        let currencyPair = "(\(currencyPair.baseSymbol)/\(currencyPair.quoteSymbol)"
+        withAnimation {
+            let favorite = Favorite(context: viewContext)
+            favorite.currencyPair = currencyPair
+
+            do {
+                try viewContext.save()
+            } catch {
+                let nsError = error as NSError
+                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
+            }
+        }
+    }
+}
+
+struct FavoriteButton_Previews: PreviewProvider {
+    static var previews: some View {
+        FavoriteButton(currencyPair: CurrencyPairModel(baseSymbol: "USD", quoteSymbol: "EUR"))
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/UI/Flag.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -0,0 +1,27 @@
+//
+//  Flag.swift
+//  Simoleon
+//
+//  Created by Dennis Concepción Martín on 24/8/21.
+//
+
+import SwiftUI
+
+struct Flag: View {
+    var flag: String
+    
+    var body: some View {
+        Image(flag)
+            .resizable()
+            .aspectRatio(contentMode: .fill)
+            .frame(width: 30, height: 30)
+            .clipShape(Circle())
+            .overlay(Circle().stroke(Color(.secondaryLabel), lineWidth: 1))
+    }
+}
+
+struct Flag_Previews: PreviewProvider {
+    static var previews: some View {
+        Flag(flag: "GB")
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/UI/LockedCurrencyPicker.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -0,0 +1,31 @@
+//
+//  LockedCurrencyPicker.swift
+//  Simoleon
+//
+//  Created by Dennis Concepción Martín on 22/07/2021.
+//
+
+import SwiftUI
+
+struct LockedCurrencyPicker: View {
+    @Environment(\.managedObjectContext) private var viewContext
+    @FetchRequest(sortDescriptors: []) private var defaultCurrency: FetchedResults<DefaultCurrency>
+    
+    var body: some View {
+        HStack {
+            Text("Default currency")
+            Spacer()
+//            Text(defaultCurrency.first?.pair ?? "USD/GBP")
+//                .foregroundColor(.secondary)
+            
+            Image(systemName: "lock")
+                .foregroundColor(.secondary)
+        }
+    }
+}
+
+struct LockedCurrencyPicker_Previews: PreviewProvider {
+    static var previews: some View {
+        LockedCurrencyPicker()
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/UI/RestoreButton.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -0,0 +1,57 @@
+//
+//  RestoreButton.swift
+//  Simoleon
+//
+//  Created by Dennis Concepción Martín on 22/07/2021.
+//
+
+import SwiftUI
+import Purchases
+
+struct RestoreButton: View {
+    @Binding var showingSubscriptionPaywall: Bool
+    @State private var alertTitle: LocalizedStringKey = ""
+    @State private var alertMessage: LocalizedStringKey = ""
+    @State private var restoringPurchases = false
+    @State private var showingAlert = false
+    
+    var body: some View {
+        Button(action: restorePurchases) {
+            if restoringPurchases {
+                ProgressView()
+            } else {
+                Text("Restore purchases")
+            }
+        }
+        .alert(isPresented: $showingAlert) {
+            Alert(title: Text(alertTitle), message: Text(alertMessage), dismissButton: .default(Text("Ok")))
+        }
+    }
+    
+    private func restorePurchases() {
+        restoringPurchases = true
+        
+        Purchases.shared.restoreTransactions { purchaserInfo, error in
+            if purchaserInfo?.entitlements["all"]?.isActive == true {
+                showingSubscriptionPaywall = false
+            } else {
+                alertTitle = LocalizedStringKey("No subscriptions found")
+                alertMessage = LocalizedStringKey("You are not subscripted to Simoleon yet.")
+                restoringPurchases = false
+                showingAlert = true
+            }
+            
+            if let error = error as NSError? {
+                alertTitle = LocalizedStringKey(error.localizedDescription)
+                alertMessage = LocalizedStringKey(error.localizedFailureReason ?? "")
+                showingAlert = true
+            }
+        }
+    }
+}
+
+struct RestoreButton_Previews: PreviewProvider {
+    static var previews: some View {
+        RestoreButton(showingSubscriptionPaywall: .constant(true))
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/UI/SearchBar.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -0,0 +1,29 @@
+//
+//  SearchBar.swift
+//  Simoleon
+//
+//  Created by Dennis Concepción Martín on 27/07/2021.
+//
+
+import SwiftUI
+
+struct SearchBar: View {
+    var placeholder: LocalizedStringKey
+    @Binding var text: String
+    
+    var body: some View {
+        TextField(placeholder, text: $text)
+            .disableAutocorrection(true)
+            .padding(10)
+            .background(
+                RoundedRectangle(cornerRadius: 15)
+                    .foregroundColor(Color(.tertiarySystemFill))
+            )
+    }
+}
+
+struct SearchBar_Previews: PreviewProvider {
+    static var previews: some View {
+        SearchBar(placeholder: "Search ...", text: .constant(""))
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/UI/Sidebar.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -0,0 +1,43 @@
+////
+////  Sidebar.swift
+////  Simoleon
+////
+////  Created by Dennis Concepción Martín on 18/07/2021.
+////
+//
+//import SwiftUI
+//
+//struct Sidebar: View {
+//    @Environment(\.managedObjectContext) private var viewContext
+//    @FetchRequest(sortDescriptors: []) private var defaultCurrency: FetchedResults<DefaultCurrency>
+//    
+//    var body: some View {
+//        List {
+//            NavigationLink(destination: Conversion()) {
+//                Label("Convert", systemImage: "arrow.counterclockwise.circle")
+//            }
+//            .accessibilityIdentifier("NavigateToConversion")
+//            
+//            NavigationLink(destination: Favorites()) {
+//                Label("Favorites", systemImage: "star")
+//            }
+//            .accessibilityIdentifier("NavigateToFavorites")
+//            
+//            NavigationLink(destination: Settings()) {
+//                Label("Settings", systemImage: "gear")
+//            }
+//            .accessibilityIdentifier("NavigateToSettings")
+//        }
+//        .listStyle(SidebarListStyle())
+//        .navigationTitle("Categories")
+//        .accessibilityIdentifier("Sidebar")
+//    }
+//}
+//
+//struct Sidebar_Previews: PreviewProvider {
+//    static var previews: some View {
+//        NavigationView {
+//            Sidebar()
+//        }
+//    }
+//}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/UI/SubscribeButton.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -0,0 +1,104 @@
+//
+//  SubscribeButton.swift
+//  Simoleon
+//
+//  Created by Dennis Concepción Martín on 22/07/2021.
+//
+
+import SwiftUI
+import Purchases
+
+struct SubscribeButton: View {
+    @Binding var showingSubscriptionPaywall: Bool
+    @State private var price = ""
+    @State private var alertTitle = ""
+    @State private var alertMessage = ""
+    @State private var showingAlert = false
+    @State private var showingPrice = false
+    
+    var body: some View {
+        Button(action: purchaseMonthlySubscription) {
+            RoundedRectangle(cornerRadius: 15)
+                .frame(height: 60)
+                .overlay(
+                    VStack {
+                        if showingPrice {
+                            Text("Subscribe for \(price) / month")
+                                .foregroundColor(.white)
+                                .fontWeight(.semibold)
+                        } else {
+                            ProgressView()
+                        }
+                    }
+                )
+        }
+        .onAppear(perform: fetchMonthlySubscription)
+        .alert(isPresented: $showingAlert) {
+            Alert(title: Text(alertTitle), message: Text(alertMessage), dismissButton: .default(Text("Ok")))
+        }
+    }
+    
+    private func fetchMonthlySubscription() {
+        Purchases.shared.offerings { (offerings, error) in
+            if let product = offerings?.current?.monthly?.product {
+                price = formatCurrency(product.priceLocale, product.price)
+                showingPrice = true
+            }
+            
+            if let error = error as NSError? {
+                alertTitle = error.localizedDescription
+                alertMessage = error.localizedFailureReason ?? ""
+                price = "-"
+                showingPrice = true
+                showingAlert = true
+            }
+        }
+    }
+    
+    private func purchaseMonthlySubscription() {
+        showingPrice = false
+        
+        Purchases.shared.offerings { (offerings, error) in
+            if let package = offerings?.current?.monthly {
+                
+                Purchases.shared.purchasePackage(package) { (transaction, purchaserInfo, error, userCancelled) in
+                    if purchaserInfo?.entitlements["all"]?.isActive == true {
+                        showingPrice = true
+                        showingSubscriptionPaywall = false
+                    }
+                    
+                    if let error = error as NSError? {
+                        alertTitle = error.localizedDescription
+                        alertMessage = error.localizedFailureReason ?? ""
+                        showingPrice = true
+                        showingAlert = true
+                    }
+                }
+                
+                if let error = error as NSError? {
+                    alertTitle = error.localizedDescription
+                    alertMessage = error.localizedFailureReason ?? ""
+                    showingPrice = true
+                    showingAlert = true
+                }
+            }
+        }
+    }
+    
+    private func formatCurrency(_ locale: Locale, _ amount: NSDecimalNumber) -> String {
+        let formatter = NumberFormatter()
+        formatter.locale = locale
+        formatter.numberStyle = .currency
+        
+        // It won't fail. Check unit test
+        let formattedAmount = formatter.string(from: amount as NSNumber)!
+        
+        return formattedAmount
+    }
+}
+
+struct SubscribeButton_Previews: PreviewProvider {
+    static var previews: some View {
+        SubscribeButton(showingSubscriptionPaywall: .constant(true))
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/UI/SubscriptionFeature.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -0,0 +1,41 @@
+//
+//  SubscriptionFeature.swift
+//  Simoleon
+//
+//  Created by Dennis Concepción Martín on 26/07/2021.
+//
+
+import SwiftUI
+
+struct SubscriptionFeature: View {
+    var symbol: String
+    var colour: Color
+    var title: LocalizedStringKey
+    var description: LocalizedStringKey
+    
+    var body: some View {
+        HStack(alignment:.top) {
+            Image(systemName: symbol)
+                .foregroundColor(colour)
+                .font(.title)
+            
+            VStack(alignment: .leading) {
+                Text(title)
+                    .font(.headline)
+                
+                Text(description)
+            }
+        }
+    }
+}
+
+struct SubscriptionFeature_Previews: PreviewProvider {
+    static var previews: some View {
+        SubscriptionFeature(
+            symbol: "star.circle.fill",
+            colour: Color(.systemYellow),
+            title: "Favorite Currencies",
+            description: "Save your favorite currencies to access them quickly."
+        )
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/UI/SubscriptionPaywall.swift	Sat Aug 28 11:15:25 2021 +0100
@@ -0,0 +1,95 @@
+//
+//  SubscriptionPaywall.swift
+//  Simoleon
+//
+//  Created by Dennis Concepción Martín on 22/07/2021.
+//
+
+import SwiftUI
+
+struct SubscriptionPaywall: View {
+    @Binding var showingSubscriptionPaywall: Bool
+    
+    var body: some View {
+        NavigationView {
+            ScrollView {
+                VStack(alignment: .leading, spacing: 20) {
+                    HStack {
+                        Spacer()
+                        VStack {
+                            Image("Subscription")
+                                .resizable()
+                                .aspectRatio(contentMode: .fit)
+                                .frame(width: 100, height: 100)
+                                .cornerRadius(25)
+                                .padding(.top)
+                            
+                            Text("Unlock All Access")
+                                .font(.title)
+                                .fontWeight(.semibold)
+                                .fixedSize(horizontal: false, vertical: true)
+                                .multilineTextAlignment(.center)
+                                .padding(.top)
+                        }
+                        
+                        Spacer()
+                    }
+                    
+                    Divider()
+                    
+                    SubscriptionFeature(
+                        symbol: "star.circle.fill",
+                        colour: Color(.systemYellow),
+                        title: "Favorite Forex Pairs",
+                        description: "Save any currency pair to access them quickly."
+                    )
+                    
+                    SubscriptionFeature(
+                        symbol: "flag.circle.fill",
+                        colour: Color(.systemRed),
+                        title: "Over 170 Currencies",
+                        description: "Access almost every currency of the world."
+                    )
+                    
+                    SubscriptionFeature(
+                        symbol: "icloud.circle.fill",
+                        colour: Color(.systemBlue),
+                        title: "Everything is Up-to-date",
+                        description: "Your settings and favorite currencies in all your devices."
+                    )
+                    
+                    SubscriptionFeature(
+                        symbol: "bitcoinsign.circle.fill",
+                        colour: Color(.systemOrange),
+                        title: "Cryptos and Commodities",
+                        description: "Convert currency between cryptos, gold, and silver."
+                    )
+                    
+                    Spacer()
+                    SubscribeButton(showingSubscriptionPaywall: $showingSubscriptionPaywall)
+                    HStack {
+                        Spacer()
+                        RestoreButton(showingSubscriptionPaywall: $showingSubscriptionPaywall)
+                        Spacer()
+                    }
+                }
+                .padding(.bottom)
+                .padding(.horizontal, 40)
+            }
+            .navigationBarTitleDisplayMode(.inline)
+            .toolbar {
+                ToolbarItem(placement: .cancellationAction) {
+                    Button(action: { showingSubscriptionPaywall = false }) {
+                        Text("Cancel")
+                    }
+                }
+            }
+        }
+    }
+}
+
+struct SubscriptionPaywall_Previews: PreviewProvider {
+    static var previews: some View {
+        SubscriptionPaywall(showingSubscriptionPaywall: .constant(true))
+    }
+}
--- a/fastlane/SnapshotHelper.swift	Wed Aug 25 11:00:21 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,309 +0,0 @@
-//
-//  SnapshotHelper.swift
-//  Example
-//
-//  Created by Felix Krause on 10/8/15.
-//
-
-// -----------------------------------------------------
-// IMPORTANT: When modifying this file, make sure to
-//            increment the version number at the very
-//            bottom of the file to notify users about
-//            the new SnapshotHelper.swift
-// -----------------------------------------------------
-
-import Foundation
-import XCTest
-
-var deviceLanguage = ""
-var locale = ""
-
-func setupSnapshot(_ app: XCUIApplication, waitForAnimations: Bool = true) {
-    Snapshot.setupSnapshot(app, waitForAnimations: waitForAnimations)
-}
-
-func snapshot(_ name: String, waitForLoadingIndicator: Bool) {
-    if waitForLoadingIndicator {
-        Snapshot.snapshot(name)
-    } else {
-        Snapshot.snapshot(name, timeWaitingForIdle: 0)
-    }
-}
-
-/// - Parameters:
-///   - name: The name of the snapshot
-///   - timeout: Amount of seconds to wait until the network loading indicator disappears. Pass `0` if you don't want to wait.
-func snapshot(_ name: String, timeWaitingForIdle timeout: TimeInterval = 20) {
-    Snapshot.snapshot(name, timeWaitingForIdle: timeout)
-}
-
-enum SnapshotError: Error, CustomDebugStringConvertible {
-    case cannotFindSimulatorHomeDirectory
-    case cannotRunOnPhysicalDevice
-
-    var debugDescription: String {
-        switch self {
-        case .cannotFindSimulatorHomeDirectory:
-            return "Couldn't find simulator home location. Please, check SIMULATOR_HOST_HOME env variable."
-        case .cannotRunOnPhysicalDevice:
-            return "Can't use Snapshot on a physical device."
-        }
-    }
-}
-
-@objcMembers
-open class Snapshot: NSObject {
-    static var app: XCUIApplication?
-    static var waitForAnimations = true
-    static var cacheDirectory: URL?
-    static var screenshotsDirectory: URL? {
-        return cacheDirectory?.appendingPathComponent("screenshots", isDirectory: true)
-    }
-
-    open class func setupSnapshot(_ app: XCUIApplication, waitForAnimations: Bool = true) {
-
-        Snapshot.app = app
-        Snapshot.waitForAnimations = waitForAnimations
-
-        do {
-            let cacheDir = try getCacheDirectory()
-            Snapshot.cacheDirectory = cacheDir
-            setLanguage(app)
-            setLocale(app)
-            setLaunchArguments(app)
-        } catch let error {
-            NSLog(error.localizedDescription)
-        }
-    }
-
-    class func setLanguage(_ app: XCUIApplication) {
-        guard let cacheDirectory = self.cacheDirectory else {
-            NSLog("CacheDirectory is not set - probably running on a physical device?")
-            return
-        }
-
-        let path = cacheDirectory.appendingPathComponent("language.txt")
-
-        do {
-            let trimCharacterSet = CharacterSet.whitespacesAndNewlines
-            deviceLanguage = try String(contentsOf: path, encoding: .utf8).trimmingCharacters(in: trimCharacterSet)
-            app.launchArguments += ["-AppleLanguages", "(\(deviceLanguage))"]
-        } catch {
-            NSLog("Couldn't detect/set language...")
-        }
-    }
-
-    class func setLocale(_ app: XCUIApplication) {
-        guard let cacheDirectory = self.cacheDirectory else {
-            NSLog("CacheDirectory is not set - probably running on a physical device?")
-            return
-        }
-
-        let path = cacheDirectory.appendingPathComponent("locale.txt")
-
-        do {
-            let trimCharacterSet = CharacterSet.whitespacesAndNewlines
-            locale = try String(contentsOf: path, encoding: .utf8).trimmingCharacters(in: trimCharacterSet)
-        } catch {
-            NSLog("Couldn't detect/set locale...")
-        }
-
-        if locale.isEmpty && !deviceLanguage.isEmpty {
-            locale = Locale(identifier: deviceLanguage).identifier
-        }
-
-        if !locale.isEmpty {
-            app.launchArguments += ["-AppleLocale", "\"\(locale)\""]
-        }
-    }
-
-    class func setLaunchArguments(_ app: XCUIApplication) {
-        guard let cacheDirectory = self.cacheDirectory else {
-            NSLog("CacheDirectory is not set - probably running on a physical device?")
-            return
-        }
-
-        let path = cacheDirectory.appendingPathComponent("snapshot-launch_arguments.txt")
-        app.launchArguments += ["-FASTLANE_SNAPSHOT", "YES", "-ui_testing"]
-
-        do {
-            let launchArguments = try String(contentsOf: path, encoding: String.Encoding.utf8)
-            let regex = try NSRegularExpression(pattern: "(\\\".+?\\\"|\\S+)", options: [])
-            let matches = regex.matches(in: launchArguments, options: [], range: NSRange(location: 0, length: launchArguments.count))
-            let results = matches.map { result -> String in
-                (launchArguments as NSString).substring(with: result.range)
-            }
-            app.launchArguments += results
-        } catch {
-            NSLog("Couldn't detect/set launch_arguments...")
-        }
-    }
-
-    open class func snapshot(_ name: String, timeWaitingForIdle timeout: TimeInterval = 20) {
-        if timeout > 0 {
-            waitForLoadingIndicatorToDisappear(within: timeout)
-        }
-
-        NSLog("snapshot: \(name)") // more information about this, check out https://docs.fastlane.tools/actions/snapshot/#how-does-it-work
-
-        if Snapshot.waitForAnimations {
-            sleep(1) // Waiting for the animation to be finished (kind of)
-        }
-
-        #if os(OSX)
-            guard let app = self.app else {
-                NSLog("XCUIApplication is not set. Please call setupSnapshot(app) before snapshot().")
-                return
-            }
-
-            app.typeKey(XCUIKeyboardKeySecondaryFn, modifierFlags: [])
-        #else
-
-            guard self.app != nil else {
-                NSLog("XCUIApplication is not set. Please call setupSnapshot(app) before snapshot().")
-                return
-            }
-
-            let screenshot = XCUIScreen.main.screenshot()
-            #if os(iOS)
-            let image = XCUIDevice.shared.orientation.isLandscape ?  fixLandscapeOrientation(image: screenshot.image) : screenshot.image
-            #else
-            let image = screenshot.image
-            #endif
-
-            guard var simulator = ProcessInfo().environment["SIMULATOR_DEVICE_NAME"], let screenshotsDir = screenshotsDirectory else { return }
-
-            do {
-                // The simulator name contains "Clone X of " inside the screenshot file when running parallelized UI Tests on concurrent devices
-                let regex = try NSRegularExpression(pattern: "Clone [0-9]+ of ")
-                let range = NSRange(location: 0, length: simulator.count)
-                simulator = regex.stringByReplacingMatches(in: simulator, range: range, withTemplate: "")
-
-                let path = screenshotsDir.appendingPathComponent("\(simulator)-\(name).png")
-                #if swift(<5.0)
-                    UIImagePNGRepresentation(image)?.write(to: path, options: .atomic)
-                #else
-                    try image.pngData()?.write(to: path, options: .atomic)
-                #endif
-            } catch let error {
-                NSLog("Problem writing screenshot: \(name) to \(screenshotsDir)/\(simulator)-\(name).png")
-                NSLog(error.localizedDescription)
-            }
-        #endif
-    }
-
-    class func fixLandscapeOrientation(image: UIImage) -> UIImage {
-        #if os(watchOS)
-            return image
-        #else
-            if #available(iOS 10.0, *) {
-                let format = UIGraphicsImageRendererFormat()
-                format.scale = image.scale
-                let renderer = UIGraphicsImageRenderer(size: image.size, format: format)
-                return renderer.image { context in
-                    image.draw(in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))
-                }
-            } else {
-                return image
-            }
-        #endif
-    }
-
-    class func waitForLoadingIndicatorToDisappear(within timeout: TimeInterval) {
-        #if os(tvOS)
-            return
-        #endif
-
-        guard let app = self.app else {
-            NSLog("XCUIApplication is not set. Please call setupSnapshot(app) before snapshot().")
-            return
-        }
-
-        let networkLoadingIndicator = app.otherElements.deviceStatusBars.networkLoadingIndicators.element
-        let networkLoadingIndicatorDisappeared = XCTNSPredicateExpectation(predicate: NSPredicate(format: "exists == false"), object: networkLoadingIndicator)
-        _ = XCTWaiter.wait(for: [networkLoadingIndicatorDisappeared], timeout: timeout)
-    }
-
-    class func getCacheDirectory() throws -> URL {
-        let cachePath = "Library/Caches/tools.fastlane"
-        // on OSX config is stored in /Users/<username>/Library
-        // and on iOS/tvOS/WatchOS it's in simulator's home dir
-        #if os(OSX)
-            let homeDir = URL(fileURLWithPath: NSHomeDirectory())
-            return homeDir.appendingPathComponent(cachePath)
-        #elseif arch(i386) || arch(x86_64) || arch(arm64)
-            guard let simulatorHostHome = ProcessInfo().environment["SIMULATOR_HOST_HOME"] else {
-                throw SnapshotError.cannotFindSimulatorHomeDirectory
-            }
-            let homeDir = URL(fileURLWithPath: simulatorHostHome)
-            return homeDir.appendingPathComponent(cachePath)
-        #else
-            throw SnapshotError.cannotRunOnPhysicalDevice
-        #endif
-    }
-}
-
-private extension XCUIElementAttributes {
-    var isNetworkLoadingIndicator: Bool {
-        if hasAllowListedIdentifier { return false }
-
-        let hasOldLoadingIndicatorSize = frame.size == CGSize(width: 10, height: 20)
-        let hasNewLoadingIndicatorSize = frame.size.width.isBetween(46, and: 47) && frame.size.height.isBetween(2, and: 3)
-
-        return hasOldLoadingIndicatorSize || hasNewLoadingIndicatorSize
-    }
-
-    var hasAllowListedIdentifier: Bool {
-        let allowListedIdentifiers = ["GeofenceLocationTrackingOn", "StandardLocationTrackingOn"]
-
-        return allowListedIdentifiers.contains(identifier)
-    }
-
-    func isStatusBar(_ deviceWidth: CGFloat) -> Bool {
-        if elementType == .statusBar { return true }
-        guard frame.origin == .zero else { return false }
-
-        let oldStatusBarSize = CGSize(width: deviceWidth, height: 20)
-        let newStatusBarSize = CGSize(width: deviceWidth, height: 44)
-
-        return [oldStatusBarSize, newStatusBarSize].contains(frame.size)
-    }
-}
-
-private extension XCUIElementQuery {
-    var networkLoadingIndicators: XCUIElementQuery {
-        let isNetworkLoadingIndicator = NSPredicate { (evaluatedObject, _) in
-            guard let element = evaluatedObject as? XCUIElementAttributes else { return false }
-
-            return element.isNetworkLoadingIndicator
-        }
-
-        return self.containing(isNetworkLoadingIndicator)
-    }
-
-    var deviceStatusBars: XCUIElementQuery {
-        guard let app = Snapshot.app else {
-            fatalError("XCUIApplication is not set. Please call setupSnapshot(app) before snapshot().")
-        }
-
-        let deviceWidth = app.windows.firstMatch.frame.width
-
-        let isStatusBar = NSPredicate { (evaluatedObject, _) in
-            guard let element = evaluatedObject as? XCUIElementAttributes else { return false }
-
-            return element.isStatusBar(deviceWidth)
-        }
-
-        return self.containing(isStatusBar)
-    }
-}
-
-private extension CGFloat {
-    func isBetween(_ numberA: CGFloat, and numberB: CGFloat) -> Bool {
-        return numberA...numberB ~= self
-    }
-}
-
-// Please don't remove the lines below
-// They are used to detect outdated configuration files
-// SnapshotHelperVersion [1.27]