changeset 158:82bd84c5973c

Implemented Favorite Button
author Dennis Concepcion Martin <dennisconcepcionmartin@gmail.com>
date Sat, 28 Aug 2021 19:17:55 +0100
parents 8c3bbd640103
children 35628bac01f5
files Simoleon.xcodeproj/project.pbxproj Simoleon.xcodeproj/xcshareddata/xcschemes/Simoleon.xcscheme Simoleon/ContentView.swift Simoleon/Models/DefaultCurrency+CoreDataClass.swift Simoleon/Models/DefaultCurrency+CoreDataProperties.swift Simoleon/Models/Favorite+CoreDataClass.swift Simoleon/Models/Favorite+CoreDataProperties.swift Simoleon/Models/FavoritePair+CoreDataClass.swift Simoleon/Models/FavoritePair+CoreDataProperties.swift Simoleon/Simoleon.xcdatamodeld/Simoleon.xcdatamodel/contents Simoleon/Tests/ButtonAnimation.swift Simoleon/UI/FavoriteButton.swift Simoleon/UI/LockedCurrencyPicker.swift
diffstat 13 files changed, 167 insertions(+), 199 deletions(-) [+]
line wrap: on
line diff
--- a/Simoleon.xcodeproj/project.pbxproj	Sat Aug 28 11:15:41 2021 +0100
+++ b/Simoleon.xcodeproj/project.pbxproj	Sat Aug 28 19:17:55 2021 +0100
@@ -22,15 +22,14 @@
 		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 */; };
 		95AA42ED26D78A4A0085570D /* FileHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95AA42EC26D78A4A0085570D /* FileHelper.swift */; };
+		95AC820726DAA3ED00CD5C3F /* FavoritePair+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95AC820526DAA3ED00CD5C3F /* FavoritePair+CoreDataClass.swift */; };
+		95AC820826DAA3ED00CD5C3F /* FavoritePair+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95AC820626DAA3ED00CD5C3F /* FavoritePair+CoreDataProperties.swift */; };
+		95AC820A26DAAC6B00CD5C3F /* ButtonAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95AC820926DAAC6B00CD5C3F /* ButtonAnimation.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 */; };
@@ -127,10 +126,6 @@
 		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>"; };
 		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>"; };
@@ -154,6 +149,9 @@
 		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>"; };
+		95AC820526DAA3ED00CD5C3F /* FavoritePair+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FavoritePair+CoreDataClass.swift"; sourceTree = "<group>"; };
+		95AC820626DAA3ED00CD5C3F /* FavoritePair+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FavoritePair+CoreDataProperties.swift"; sourceTree = "<group>"; };
+		95AC820926DAAC6B00CD5C3F /* ButtonAnimation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonAnimation.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>"; };
@@ -255,10 +253,8 @@
 		95559331269B094A000FD726 /* Models */ = {
 			isa = PBXGroup;
 			children = (
-				95851CEE26D4E89C004ADA79 /* Favorite+CoreDataClass.swift */,
-				95851CEF26D4E89C004ADA79 /* Favorite+CoreDataProperties.swift */,
-				95851CE626D4E552004ADA79 /* DefaultCurrency+CoreDataClass.swift */,
-				95851CE726D4E552004ADA79 /* DefaultCurrency+CoreDataProperties.swift */,
+				95AC820526DAA3ED00CD5C3F /* FavoritePair+CoreDataClass.swift */,
+				95AC820626DAA3ED00CD5C3F /* FavoritePair+CoreDataProperties.swift */,
 				95AEBCA226A0900E00613729 /* CurrencyQuoteModel.swift */,
 				953B8B1626D3A970003CF530 /* CurrencyDetailsModel.swift */,
 				957DCF3226D7ADEA00BCAB1E /* CurrencyPairModel.swift */,
@@ -420,6 +416,7 @@
 			isa = PBXGroup;
 			children = (
 				95DA4B5826D7E2DE00566C5E /* ChildListResets.swift */,
+				95AC820926DAAC6B00CD5C3F /* ButtonAnimation.swift */,
 			);
 			path = Tests;
 			sourceTree = "<group>";
@@ -687,13 +684,12 @@
 			buildActionMask = 2147483647;
 			files = (
 				95C5179926A5EC9F00BC2B24 /* FavoriteButton.swift in Sources */,
-				95851CF126D4E89C004ADA79 /* Favorite+CoreDataProperties.swift in Sources */,
+				95AC820726DAA3ED00CD5C3F /* FavoritePair+CoreDataClass.swift in Sources */,
 				9522CD9D26CED2E100DD9D03 /* ErrorHandling.swift in Sources */,
 				95AA42ED26D78A4A0085570D /* FileHelper.swift in Sources */,
 				95851CE326D4DAAE004ADA79 /* CurrencyButton.swift in Sources */,
 				95C5B2312697752700941585 /* Persistence.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 */,
@@ -701,13 +697,14 @@
 				9522BEA926B5A4D20076B098 /* AppDelegate.swift in Sources */,
 				95851CE526D4DB4C004ADA79 /* Flag.swift in Sources */,
 				95D8C8CD26A9784500BCC188 /* SubscribeButton.swift in Sources */,
-				95851CE926D4E552004ADA79 /* DefaultCurrency+CoreDataProperties.swift in Sources */,
 				9585BB1A26A6E8FD00E3193E /* HapticsHelper.swift in Sources */,
 				95D8C8CF26A98A7900BCC188 /* RestoreButton.swift in Sources */,
 				95C5179F26A5F34200BC2B24 /* FavoritesView.swift in Sources */,
 				95C5B2282697752600941585 /* SimoleonApp.swift in Sources */,
 				95B54F4A26A4A450001DC0D8 /* ConversionBox.swift in Sources */,
 				95D8C8C726A95D2900BCC188 /* SubscriptionPaywall.swift in Sources */,
+				95AC820A26DAAC6B00CD5C3F /* ButtonAnimation.swift in Sources */,
+				95AC820826DAA3ED00CD5C3F /* FavoritePair+CoreDataProperties.swift in Sources */,
 				95D8C8D126A9BC6200BCC188 /* LockedCurrencyPicker.swift in Sources */,
 				95C517A126A5F6C000BC2B24 /* ResignKeyboard.swift in Sources */,
 				95CE6A3626D50B7700D9DCBD /* CurrencyList.swift in Sources */,
@@ -722,7 +719,6 @@
 				95C5179126A5DC8E00BC2B24 /* ConditionalWrapper.swift in Sources */,
 				95B54F5126A4ACAC001DC0D8 /* Sidebar.swift in Sources */,
 				95561E3F26AF25EF00CCB543 /* SubscriptionFeature.swift in Sources */,
-				95851CE826D4E552004ADA79 /* DefaultCurrency+CoreDataClass.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
--- a/Simoleon.xcodeproj/xcshareddata/xcschemes/Simoleon.xcscheme	Sat Aug 28 11:15:41 2021 +0100
+++ b/Simoleon.xcodeproj/xcshareddata/xcschemes/Simoleon.xcscheme	Sat Aug 28 19:17:55 2021 +0100
@@ -104,8 +104,7 @@
       ignoresPersistentStateOnLaunch = "NO"
       debugDocumentVersioning = "YES"
       debugServiceExtension = "internal"
-      allowLocationSimulation = "YES"
-      showNonLocalizedStrings = "YES">
+      allowLocationSimulation = "YES">
       <BuildableProductRunnable
          runnableDebuggingMode = "0">
          <BuildableReference
--- a/Simoleon/ContentView.swift	Sat Aug 28 11:15:41 2021 +0100
+++ b/Simoleon/ContentView.swift	Sat Aug 28 19:17:55 2021 +0100
@@ -8,8 +8,6 @@
 import SwiftUI
 
 struct ContentView: View {
-    @Environment(\.managedObjectContext) private var viewContext
-    @FetchRequest(sortDescriptors: []) private var defaultCurrency: FetchedResults<DefaultCurrency>
     @State private var tab: Tab = .convert
     
     private enum Tab {
--- a/Simoleon/Models/DefaultCurrency+CoreDataClass.swift	Sat Aug 28 11:15:41 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-//
-//  DefaultCurrency+CoreDataClass.swift
-//  Simoleon
-//
-//  Created by Dennis Concepción Martín on 24/8/21.
-//
-//
-
-import Foundation
-import CoreData
-
-@objc(DefaultCurrency)
-public class DefaultCurrency: NSManagedObject {
-
-}
--- a/Simoleon/Models/DefaultCurrency+CoreDataProperties.swift	Sat Aug 28 11:15:41 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,26 +0,0 @@
-//
-//  DefaultCurrency+CoreDataProperties.swift
-//  Simoleon
-//
-//  Created by Dennis Concepción Martín on 24/8/21.
-//
-//
-
-import Foundation
-import CoreData
-
-
-extension DefaultCurrency {
-
-    @nonobjc public class func fetchRequest() -> NSFetchRequest<DefaultCurrency> {
-        return NSFetchRequest<DefaultCurrency>(entityName: "DefaultCurrency")
-    }
-
-    @NSManaged public var firstSymbol: String
-    @NSManaged public var secondSymbol: String
-
-}
-
-extension DefaultCurrency : Identifiable {
-
-}
--- a/Simoleon/Models/Favorite+CoreDataClass.swift	Sat Aug 28 11:15:41 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-//
-//  Favorite+CoreDataClass.swift
-//  Simoleon
-//
-//  Created by Dennis Concepción Martín on 24/8/21.
-//
-//
-
-import Foundation
-import CoreData
-
-@objc(Favorite)
-public class Favorite: NSManagedObject {
-
-}
--- a/Simoleon/Models/Favorite+CoreDataProperties.swift	Sat Aug 28 11:15:41 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-//
-//  Favorite+CoreDataProperties.swift
-//  Simoleon
-//
-//  Created by Dennis Concepción Martín on 24/8/21.
-//
-//
-
-import Foundation
-import CoreData
-
-
-extension Favorite {
-
-    @nonobjc public class func fetchRequest() -> NSFetchRequest<Favorite> {
-        return NSFetchRequest<Favorite>(entityName: "Favorite")
-    }
-
-    @NSManaged public var currencyPair: String
-
-}
-
-extension Favorite : Identifiable {
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/Models/FavoritePair+CoreDataClass.swift	Sat Aug 28 19:17:55 2021 +0100
@@ -0,0 +1,15 @@
+//
+//  FavoritePair+CoreDataClass.swift
+//  Simoleon
+//
+//  Created by Dennis Concepción Martín on 28/8/21.
+//
+//
+
+import Foundation
+import CoreData
+
+@objc(FavoritePair)
+public class FavoritePair: NSManagedObject {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/Models/FavoritePair+CoreDataProperties.swift	Sat Aug 28 19:17:55 2021 +0100
@@ -0,0 +1,26 @@
+//
+//  FavoritePair+CoreDataProperties.swift
+//  Simoleon
+//
+//  Created by Dennis Concepción Martín on 28/8/21.
+//
+//
+
+import Foundation
+import CoreData
+
+
+extension FavoritePair {
+
+    @nonobjc public class func fetchRequest() -> NSFetchRequest<FavoritePair> {
+        return NSFetchRequest<FavoritePair>(entityName: "FavoritePair")
+    }
+
+    @NSManaged public var baseSymbol: String?
+    @NSManaged public var quoteSymbol: String?
+
+}
+
+extension FavoritePair : Identifiable {
+
+}
--- a/Simoleon/Simoleon.xcdatamodeld/Simoleon.xcdatamodel/contents	Sat Aug 28 11:15:41 2021 +0100
+++ b/Simoleon/Simoleon.xcdatamodeld/Simoleon.xcdatamodel/contents	Sat Aug 28 19:17:55 2021 +0100
@@ -1,14 +1,10 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <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="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 name="FavoritePair" representedClassName="FavoritePair" syncable="YES">
+        <attribute name="baseSymbol" optional="YES" attributeType="String"/>
+        <attribute name="quoteSymbol" optional="YES" attributeType="String"/>
     </entity>
     <elements>
-        <element name="DefaultCurrency" positionX="0" positionY="0" width="128" height="59"/>
-        <element name="Favorite" positionX="0" positionY="0" width="128" height="44"/>
+        <element name="FavoritePair" positionX="-63" positionY="-18" width="128" height="59"/>
     </elements>
 </model>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Simoleon/Tests/ButtonAnimation.swift	Sat Aug 28 19:17:55 2021 +0100
@@ -0,0 +1,31 @@
+//
+//  ButtonAnimation.swift
+//  Simoleon
+//
+//  Created by Dennis Concepción Martín on 28/8/21.
+//
+
+import SwiftUI
+
+struct ButtonAnimation: View {
+    @State private var scale: CGFloat = 1
+    
+    var body: some View {
+        Button("Press here", action: animate)
+            .scaleEffect(scale)
+            .animation(.easeIn, value: scale)
+    }
+    
+    private func animate() {
+        scale += 0.2
+        DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
+            scale -= 0.2
+        }
+    }
+}
+
+struct ButtonAnimation_Previews: PreviewProvider {
+    static var previews: some View {
+        ButtonAnimation()
+    }
+}
--- a/Simoleon/UI/FavoriteButton.swift	Sat Aug 28 11:15:41 2021 +0100
+++ b/Simoleon/UI/FavoriteButton.swift	Sat Aug 28 19:17:55 2021 +0100
@@ -9,94 +9,82 @@
 
 struct FavoriteButton: View {
     @State var currencyPair: CurrencyPairModel
+    @State private var scale: CGFloat = 1
     @Environment(\.managedObjectContext) private var viewContext
-    @FetchRequest(sortDescriptors: []) private var favorites: FetchedResults<Favorite>
-    @State private var starSymbol = "star"
+    @FetchRequest(sortDescriptors: []) private var favoritePairs: FetchedResults<FavoritePair>
     
     var body: some View {
-        Button(action: favoriteAction) {
+        Button(action: {
+            animate()
+            if isFavorite() {
+                remove()
+            } else {
+                add()
+            }
+        }) {
             RoundedRectangle(cornerRadius: 15)
                 .foregroundColor(Color(.secondarySystemBackground))
                 .frame(width: 60, height: 60)
                 .overlay(
-                    Image(systemName: generateStar())
-                        .font(.system(size: 28))
-                        .foregroundColor(Color(.systemYellow))
+                    VStack {
+                        if isFavorite() {
+                            Image(systemName: "star.fill")
+                        } else {
+                            Image(systemName: "star")
+                        }
+                    }
+                    .font(.system(size: 28))
+                    .foregroundColor(Color(.systemYellow))
                 )
         }
-        .accessibilityIdentifier("AddToFavorites")
+        .scaleEffect(scale)
+        .animation(.linear(duration: 0.2), value: scale)
     }
     
-    /*
-     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()
-        }
+    func add() {
+        let favoritePair = FavoritePair(context: viewContext)
+        favoritePair.baseSymbol = currencyPair.baseSymbol
+        favoritePair.quoteSymbol = currencyPair.quoteSymbol
         
-        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"
+        do {
+            try viewContext.save()
+        } catch {
+            let nsError = error as NSError
+            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
         }
     }
     
-    /*
-     * 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)")
-            }
+    func remove() {
+        let favoritePair = favoritePairs.first(
+            where: {
+                $0.baseSymbol == currencyPair.baseSymbol && $0.quoteSymbol == currencyPair.quoteSymbol
+        })
+        
+        viewContext.delete(favoritePair!)
+        
+        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)")
-            }
+    func isFavorite() -> Bool {
+        let favoritePair = favoritePairs.first(
+            where: {
+                $0.baseSymbol == currencyPair.baseSymbol && $0.quoteSymbol == currencyPair.quoteSymbol
+        })
+        
+        guard let _ = favoritePair else { return false }
+        
+        return true
+    }
+    
+    private func animate() {
+        scale += 0.2
+        DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
+            scale -= 0.2
         }
     }
 }
--- a/Simoleon/UI/LockedCurrencyPicker.swift	Sat Aug 28 11:15:41 2021 +0100
+++ b/Simoleon/UI/LockedCurrencyPicker.swift	Sat Aug 28 19:17:55 2021 +0100
@@ -7,25 +7,25 @@
 
 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()
+//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()
-    }
-}
+//            
+//            Image(systemName: "lock")
+//                .foregroundColor(.secondary)
+//        }
+//    }
+//}
+//
+//struct LockedCurrencyPicker_Previews: PreviewProvider {
+//    static var previews: some View {
+//        LockedCurrencyPicker()
+//    }
+//}