Mercurial > public > simoleon
changeset 42:d25b02d439d4
Minor updates subscription and legal requirements
author | Dennis Concepción Martín <dennisconcepcionmartin@gmail.com> |
---|---|
date | Mon, 26 Jul 2021 15:35:06 +0100 |
parents | 7703c122ce96 |
children | 2eb05f396fcd |
files | Simoleon.xcodeproj/project.pbxproj Simoleon.xcodeproj/project.xcworkspace/xcuserdata/dennis.xcuserdatad/UserInterfaceState.xcuserstate Simoleon/ContentView.swift Simoleon/ContentViewPad.swift Simoleon/Conversion.swift Simoleon/Favourites.swift Simoleon/Functions/SimpleSuccess.swift Simoleon/Helpers/ConversionBox.swift Simoleon/Helpers/CurrencySelector.swift Simoleon/Helpers/FavouriteButton.swift Simoleon/Helpers/RestoreButton.swift Simoleon/Helpers/Sidebar.swift Simoleon/Helpers/SubscribeButton.swift Simoleon/Helpers/SubscriberInfo.swift Simoleon/Helpers/SubscriptionController.swift Simoleon/Settings.swift Simoleon/Subscription.swift Simoleon/SubscriptionPaywall.swift SimoleonTests/SimoleonTests.swift |
diffstat | 19 files changed, 255 insertions(+), 377 deletions(-) [+] |
line wrap: on
line diff
--- a/Simoleon.xcodeproj/project.pbxproj Sun Jul 25 10:59:51 2021 +0100 +++ b/Simoleon.xcodeproj/project.pbxproj Mon Jul 26 15:35:06 2021 +0100 @@ -43,12 +43,10 @@ 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 */; }; - 95D8C8C726A95D2900BCC188 /* Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95D8C8C626A95D2900BCC188 /* Subscription.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 */; }; - 95D8C8D326A9C17300BCC188 /* SubscriptionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95D8C8D226A9C17300BCC188 /* SubscriptionController.swift */; }; - 95D8C8D526A9E20F00BCC188 /* SubscriberInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95D8C8D426A9E20F00BCC188 /* SubscriberInfo.swift */; }; 95DD4ABB269B33810027CA1F /* CurrencyPairs.json in Resources */ = {isa = PBXBuildFile; fileRef = 95DD4ABA269B33810027CA1F /* CurrencyPairs.json */; }; 95E76436269DFC1A008E9F31 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 95E76435269DFC1A008E9F31 /* LaunchScreen.storyboard */; }; 95E7643A269E0037008E9F31 /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 95E76439269E0037008E9F31 /* CloudKit.framework */; }; @@ -116,12 +114,10 @@ 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>"; }; - 95D8C8C626A95D2900BCC188 /* Subscription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Subscription.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>"; }; - 95D8C8D226A9C17300BCC188 /* SubscriptionController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionController.swift; sourceTree = "<group>"; }; - 95D8C8D426A9E20F00BCC188 /* SubscriberInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriberInfo.swift; sourceTree = "<group>"; }; 95DD4ABA269B33810027CA1F /* CurrencyPairs.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = CurrencyPairs.json; 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>"; }; @@ -279,7 +275,7 @@ 95B54F4326A4842C001DC0D8 /* Conversion.swift */, 95C5179E26A5F34200BC2B24 /* Favourites.swift */, 957065E126A5FE0400523E68 /* Settings.swift */, - 95D8C8C626A95D2900BCC188 /* Subscription.swift */, + 95D8C8C626A95D2900BCC188 /* SubscriptionPaywall.swift */, 95C5B22B2697752700941585 /* Assets.xcassets */, 95C5B2302697752700941585 /* Persistence.swift */, 95C5B2352697752700941585 /* Info.plist */, @@ -344,8 +340,6 @@ 95D8C8CC26A9784500BCC188 /* SubscribeButton.swift */, 95D8C8CE26A98A7900BCC188 /* RestoreButton.swift */, 95D8C8D026A9BC6200BCC188 /* LockedCurrencyPicker.swift */, - 95D8C8D226A9C17300BCC188 /* SubscriptionController.swift */, - 95D8C8D426A9E20F00BCC188 /* SubscriberInfo.swift */, ); path = Helpers; sourceTree = "<group>"; @@ -493,7 +487,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 95D8C8D326A9C17300BCC188 /* SubscriptionController.swift in Sources */, 95C5179926A5EC9F00BC2B24 /* FavouriteButton.swift in Sources */, 95C5179C26A5EFBE00BC2B24 /* Favourite+CoreDataClass.swift in Sources */, 950A377826A820F800CAB175 /* DefaultCurrency+CoreDataClass.swift in Sources */, @@ -501,7 +494,6 @@ 9585BB1226A6B71B00E3193E /* ReadConfig.swift in Sources */, 95AEBC9526A03ECB00613729 /* ContentView.swift in Sources */, 95AEBC9B26A04A4200613729 /* CurrencyMetadataModel.swift in Sources */, - 95D8C8D526A9E20F00BCC188 /* SubscriberInfo.swift in Sources */, 95D8C8CD26A9784500BCC188 /* SubscribeButton.swift in Sources */, 950A377726A820F800CAB175 /* DefaultCurrency+CoreDataProperties.swift in Sources */, 9585BB1A26A6E8FD00E3193E /* SimpleSuccess.swift in Sources */, @@ -511,7 +503,7 @@ 95C5179F26A5F34200BC2B24 /* Favourites.swift in Sources */, 95C5B2282697752600941585 /* SimoleonApp.swift in Sources */, 95B54F4A26A4A450001DC0D8 /* ConversionBox.swift in Sources */, - 95D8C8C726A95D2900BCC188 /* Subscription.swift in Sources */, + 95D8C8C726A95D2900BCC188 /* SubscriptionPaywall.swift in Sources */, 95D8C8D126A9BC6200BCC188 /* LockedCurrencyPicker.swift in Sources */, 95C517A126A5F6C000BC2B24 /* ResignKeyboard.swift in Sources */, 95AEBC9D26A04D4600613729 /* CurrencyRow.swift in Sources */, @@ -707,7 +699,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Simoleon/Simoleon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 11; + CURRENT_PROJECT_VERSION = 13; DEVELOPMENT_ASSET_PATHS = "\"Simoleon/Preview Content\""; DEVELOPMENT_TEAM = MTX83R5H8X; ENABLE_PREVIEWS = YES; @@ -732,7 +724,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Simoleon/Simoleon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 11; + CURRENT_PROJECT_VERSION = 13; DEVELOPMENT_ASSET_PATHS = "\"Simoleon/Preview Content\""; DEVELOPMENT_TEAM = MTX83R5H8X; ENABLE_PREVIEWS = YES;
Binary file Simoleon.xcodeproj/project.xcworkspace/xcuserdata/dennis.xcuserdatad/UserInterfaceState.xcuserstate has changed
--- a/Simoleon/ContentView.swift Sun Jul 25 10:59:51 2021 +0100 +++ b/Simoleon/ContentView.swift Mon Jul 26 15:35:06 2021 +0100 @@ -6,16 +6,15 @@ // import SwiftUI -import Purchases struct ContentView: View { @State private var tab: Tab = .convert - @StateObject var subscriptionController = SubscriptionController() + @Environment(\.managedObjectContext) private var viewContext + @FetchRequest(sortDescriptors: []) private var defaultCurrency: FetchedResults<DefaultCurrency> var body: some View { TabView(selection: $tab) { - Conversion(fetchUserSettings: true, currencyPair: "USD/GBP") - .environmentObject(subscriptionController) + Conversion(currencyPair: defaultCurrency.first?.pair ?? "USD/GBP") .tabItem { Text("Convert", comment: "Tab bar button to show conversion") Image(systemName: "arrow.counterclockwise.circle") @@ -23,7 +22,6 @@ .tag(Tab.convert) Favourites() - .environmentObject(subscriptionController) .tabItem { Text("Favourites", comment: "Tab bar button to show favourites") Image(systemName: "star") @@ -31,24 +29,12 @@ .tag(Tab.favourites) Settings() - .environmentObject(subscriptionController) .tabItem { Text("Settings", comment: "Tab bar button to show settings") Image(systemName: "gear") } .tag(Tab.settings) } - .onAppear(perform: checkEntitlements) - } - - private func checkEntitlements() { - Purchases.shared.purchaserInfo { (purchaserInfo, error) in - if purchaserInfo?.entitlements["all"]?.isActive == true { - self.subscriptionController.isActive = true - } else { - // User subscription is not active - } - } } private enum Tab { @@ -59,6 +45,5 @@ struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() -// .environment(\.locale, .init(identifier: "es")) } }
--- a/Simoleon/ContentViewPad.swift Sun Jul 25 10:59:51 2021 +0100 +++ b/Simoleon/ContentViewPad.swift Mon Jul 26 15:35:06 2021 +0100 @@ -6,29 +6,15 @@ // import SwiftUI -import Purchases struct ContentViewPad: View { - @StateObject var subscriptionController = SubscriptionController() + @Environment(\.managedObjectContext) private var viewContext + @FetchRequest(sortDescriptors: []) private var defaultCurrency: FetchedResults<DefaultCurrency> var body: some View { NavigationView { Sidebar() - .environmentObject(subscriptionController) - - Conversion(fetchUserSettings: true, currencyPair: "USD/GBP") - .environmentObject(subscriptionController) - } - .onAppear(perform: checkEntitlements) - } - - private func checkEntitlements() { - Purchases.shared.purchaserInfo { (purchaserInfo, error) in - if purchaserInfo?.entitlements["all"]?.isActive == true { - self.subscriptionController.isActive = true - } else { - // User subscription is not active - } + Conversion(currencyPair: defaultCurrency.first?.pair ?? "USD/GBP") } } }
--- a/Simoleon/Conversion.swift Sun Jul 25 10:59:51 2021 +0100 +++ b/Simoleon/Conversion.swift Mon Jul 26 15:35:06 2021 +0100 @@ -6,20 +6,15 @@ // import SwiftUI +import Purchases struct Conversion: View { - var fetchUserSettings: Bool @State var currencyPair: String - @EnvironmentObject var subscriptionController: SubscriptionController - @State private var amountToConvert = "1000" @State private var price: Double = 1.00 @State private var showingConversion = false @State private var showingCurrencySelector = false - @State private var isEditing = false - - @Environment(\.managedObjectContext) private var viewContext - @FetchRequest(sortDescriptors: []) private var defaultCurrency: FetchedResults<DefaultCurrency> + @State private var amountIsEditing = false let currencyMetadata: [String: CurrencyMetadataModel] = parseJson("CurrencyMetadata.json") @@ -31,7 +26,10 @@ RoundedRectangle(cornerRadius: 15) .foregroundColor(Color(.secondarySystemBackground)) .frame(height: 60) - .overlay(CurrencyRow(currencyPair: currencyPair).padding(.horizontal)) + .overlay( + CurrencyRow(currencyPair: currencyPair) + .padding(.horizontal) + ) } FavouriteButton(currencyPair: currencyPair) @@ -42,47 +40,34 @@ amountToConvert: $amountToConvert, price: $price, showingConversion: $showingConversion, - showingCurrencySelector: $showingCurrencySelector, - isEditing: $isEditing + amountIsEditing: $amountIsEditing ) } .padding() - .onAppear { - if fetchUserSettings { - fetchingUserSettings() - } - - request(currencyPair) - } - .onChange(of: showingCurrencySelector, perform: { showingCurrencySelector in - if !showingCurrencySelector { - request(currencyPair) - } - }) .sheet(isPresented: $showingCurrencySelector) { CurrencySelector(currencyPair: $currencyPair, showingCurrencySelector: $showingCurrencySelector) - .environmentObject(subscriptionController) } } + .onAppear(perform: request) .navigationTitle(Text("Convert", comment: "Navigation title")) .toolbar { ToolbarItem(placement: .cancellationAction) { - if isEditing { + if amountIsEditing { Button(action: { UIApplication.shared.dismissKeyboard() - isEditing = false + amountIsEditing = false }) { Text("Cancel", comment: "Button to stop editing textfield") } } } } - .if(UIDevice.current.userInterfaceIdiom == .phone && fetchUserSettings) { content in + .if(UIDevice.current.userInterfaceIdiom == .phone) { content in NavigationView { content } } } - private func request(_ currencyPair: String) { + private func request() { let url = "\(readConfig("API_URL")!)quotes?pairs=\(currencyPair)&api_key=\(readConfig("API_KEY")!)" Simoleon.request(url: url, model: [CurrencyQuoteModel].self) { response in @@ -95,21 +80,11 @@ } } } - - /* - 1) Fetch default currency from User Settings - 2) Change State var currencyPair - */ - private func fetchingUserSettings() { - if let defaultCurrency = defaultCurrency.first { - self.currencyPair = defaultCurrency.pair ?? "USD/GBP" - } - } } struct Conversion_Previews: PreviewProvider { static var previews: some View { - Conversion(fetchUserSettings: true, currencyPair: "USD/GBP") + Conversion(currencyPair: "USD/GBP") } }
--- a/Simoleon/Favourites.swift Sun Jul 25 10:59:51 2021 +0100 +++ b/Simoleon/Favourites.swift Mon Jul 26 15:35:06 2021 +0100 @@ -11,9 +11,7 @@ @Environment(\.managedObjectContext) private var viewContext @FetchRequest( sortDescriptors: [NSSortDescriptor(keyPath: \Favourite.currencyPair, ascending: true)], - animation: .default) - private var favourite: FetchedResults<Favourite> - @EnvironmentObject var subscriptionController: SubscriptionController + animation: .default) private var favourite: FetchedResults<Favourite> var body: some View { VStack { @@ -27,9 +25,7 @@ } else { List { ForEach(favourite) { favourite in - NavigationLink(destination: Conversion(fetchUserSettings: false, currencyPair: favourite.currencyPair) - .environmentObject(subscriptionController) - ) { + NavigationLink(destination: Conversion(currencyPair: favourite.currencyPair)) { CurrencyRow(currencyPair: favourite.currencyPair) } }
--- a/Simoleon/Functions/SimpleSuccess.swift Sun Jul 25 10:59:51 2021 +0100 +++ b/Simoleon/Functions/SimpleSuccess.swift Mon Jul 26 15:35:06 2021 +0100 @@ -7,6 +7,9 @@ import SwiftUI +/* + Haptics + */ func simpleSuccess() { let generator = UINotificationFeedbackGenerator() generator.notificationOccurred(.success)
--- a/Simoleon/Helpers/ConversionBox.swift Sun Jul 25 10:59:51 2021 +0100 +++ b/Simoleon/Helpers/ConversionBox.swift Mon Jul 26 15:35:06 2021 +0100 @@ -12,8 +12,7 @@ @Binding var amountToConvert: String @Binding var price: Double @Binding var showingConversion: Bool - @Binding var showingCurrencySelector: Bool - @Binding var isEditing: Bool + @Binding var amountIsEditing: Bool let currencyMetadata: [String: CurrencyMetadataModel] = parseJson("CurrencyMetadata.json") @@ -29,13 +28,13 @@ TextField("Enter amount", text: $amountToConvert) { startedEditing in if startedEditing { withAnimation { - isEditing = true + amountIsEditing = true } } } onCommit: { withAnimation { - isEditing = false + amountIsEditing = false } } .keyboardType(.decimalPad) @@ -63,7 +62,10 @@ } } - + /* + if the amount can be converted to Double -> return amount + else -> return zero + */ private func makeConversion() -> Double { if let amountToConvert = Double(amountToConvert) { return amountToConvert * price /// Conversion @@ -76,6 +78,12 @@ struct ConversionBox_Previews: PreviewProvider { static var previews: some View { - ConversionBox(currencyPair: .constant("USD/GBP"), amountToConvert: .constant("1000"), price: .constant(1), showingConversion: .constant(false), showingCurrencySelector: .constant(false), isEditing: .constant(false)) + ConversionBox( + currencyPair: .constant("USD/GBP"), + amountToConvert: .constant("1000"), + price: .constant(1), + showingConversion: .constant(false), + amountIsEditing: .constant(false) + ) } }
--- a/Simoleon/Helpers/CurrencySelector.swift Sun Jul 25 10:59:51 2021 +0100 +++ b/Simoleon/Helpers/CurrencySelector.swift Mon Jul 26 15:35:06 2021 +0100 @@ -11,7 +11,6 @@ struct CurrencySelector: View { @Binding var currencyPair: String @Binding var showingCurrencySelector: Bool - @EnvironmentObject var subscriptionController: SubscriptionController @State private var searchCurrency = "" @State private var showingSubscriptionPaywall = false @@ -34,6 +33,9 @@ UIApplication.shared.dismissKeyboard() }) ) + .sheet(isPresented: $showingSubscriptionPaywall) { + SubscriptionPaywall(showingSubscriptionPaywall: $showingSubscriptionPaywall) + } .navigationTitle(Text("Currencies", comment: "Navigation title")) .navigationBarTitleDisplayMode(.inline) .toolbar { @@ -44,11 +46,12 @@ } } } - .sheet(isPresented: $showingSubscriptionPaywall) { - Subscription(showingSubscriptionPaywall: $showingSubscriptionPaywall) - } } + /* + If searched currency string is empty -> show all currencies + else -> show filtered list of currencies containing searched currency string + */ private func currencyPairs() -> [String] { let currencyPairs: [String] = parseJson("CurrencyPairs.json") @@ -60,12 +63,18 @@ } + /* + If user is subscribed -> select currency and dismiss currency selector + else -> show subscription paywall + */ private func select(_ currencyPair: String) { - if subscriptionController.isActive { - self.currencyPair = currencyPair - showingCurrencySelector = false - } else { - showingSubscriptionPaywall = true + Purchases.shared.purchaserInfo { (purchaserInfo, error) in + if purchaserInfo?.entitlements["all"]?.isActive == true { + self.currencyPair = currencyPair + showingCurrencySelector = false + } else { + showingSubscriptionPaywall = true + } } } } @@ -74,6 +83,5 @@ struct CurrencySelector_Previews: PreviewProvider { static var previews: some View { CurrencySelector(currencyPair: .constant("USD/GBP"), showingCurrencySelector: .constant(false)) - .environmentObject(SubscriptionController()) } }
--- a/Simoleon/Helpers/FavouriteButton.swift Sun Jul 25 10:59:51 2021 +0100 +++ b/Simoleon/Helpers/FavouriteButton.swift Mon Jul 26 15:35:06 2021 +0100 @@ -9,49 +9,55 @@ struct FavouriteButton: View { var currencyPair: String + + @State private var starSymbol = "star" @Environment(\.managedObjectContext) private var viewContext @FetchRequest(sortDescriptors: []) private var favourite: FetchedResults<Favourite> var body: some View { - Button(action: { - if isFavourite() { - removeFromFavourites() - simpleSuccess() - } else { - addToFavourites() - simpleSuccess() - } - }) { + let favouriteCurrencyPairs = favourite.map { $0.currencyPair } + Button(action: { favouriteAction(favouriteCurrencyPairs) }) { RoundedRectangle(cornerRadius: 15) .foregroundColor(Color(.secondarySystemBackground)) .frame(width: 60, height: 60) .overlay( - Image(systemName: generateStar()) + Image(systemName: generateStar(favouriteCurrencyPairs)) .font(.system(size: 28)) .foregroundColor(Color(.systemYellow)) - ) } } - private func isFavourite() -> Bool { - let favouriteCurrencyPairs = favourite.map { $0.currencyPair } - + /* + If currency pair is favourite -> button action is to remove from favourites + else -> button action is to add to favourites + */ + private func favouriteAction(_ favouriteCurrencyPairs: [String]) { if favouriteCurrencyPairs.contains(currencyPair) { - return true + removeFromFavourites() } else { - return false + addToFavourites() } + + simpleSuccess() } - private func generateStar() -> String { - if isFavourite() { + /* + if currency pair is favourite -> return "star.fill" symbol + else -> return "star" + */ + private func generateStar(_ favouriteCurrencyPairs: [String]) -> String { + if favouriteCurrencyPairs.contains(currencyPair) { return "star.fill" } else { return "star" } } + /* + 1) Get first favourite core data object that matches the specified currency pair + 2) Delete it + */ private func removeFromFavourites() { withAnimation { let favouriteObject = favourite.first(where: { $0.currencyPair == currencyPair }) @@ -66,6 +72,10 @@ } } + /* + 1) Create a favourite core data object + 2) Save it + */ private func addToFavourites() { withAnimation { let favourite = Favourite(context: viewContext)
--- a/Simoleon/Helpers/RestoreButton.swift Sun Jul 25 10:59:51 2021 +0100 +++ b/Simoleon/Helpers/RestoreButton.swift Mon Jul 26 15:35:06 2021 +0100 @@ -10,7 +10,6 @@ struct RestoreButton: View { @Binding var showingSubscriptionPaywall: Bool - @EnvironmentObject var subscriptionController: SubscriptionController @State private var alertTitle: LocalizedStringKey = "" @State private var alertMessage: LocalizedStringKey = "" @@ -35,7 +34,6 @@ Purchases.shared.restoreTransactions { purchaserInfo, error in if purchaserInfo?.entitlements["all"]?.isActive == true { - subscriptionController.isActive = true showingSubscriptionPaywall = false } else { alertTitle = LocalizedStringKey("No subscriptions found")
--- a/Simoleon/Helpers/Sidebar.swift Sun Jul 25 10:59:51 2021 +0100 +++ b/Simoleon/Helpers/Sidebar.swift Mon Jul 26 15:35:06 2021 +0100 @@ -8,25 +8,17 @@ import SwiftUI struct Sidebar: View { - @EnvironmentObject var subscriptionController: SubscriptionController - var body: some View { List { - NavigationLink(destination: Conversion(fetchUserSettings: true, currencyPair: "USD/GBP") - .environmentObject(subscriptionController) - ) { + NavigationLink(destination: Conversion(currencyPair: "USD/GBP")) { Label("Convert", systemImage: "arrow.counterclockwise.circle") } - NavigationLink(destination: Favourites() - .environmentObject(subscriptionController) - ) { + NavigationLink(destination: Favourites()) { Label("Favourites", systemImage: "star") } - NavigationLink(destination: Settings() - .environmentObject(subscriptionController) - ) { + NavigationLink(destination: Settings()) { Label("Settings", systemImage: "gear") } }
--- a/Simoleon/Helpers/SubscribeButton.swift Sun Jul 25 10:59:51 2021 +0100 +++ b/Simoleon/Helpers/SubscribeButton.swift Mon Jul 26 15:35:06 2021 +0100 @@ -10,7 +10,6 @@ struct SubscribeButton: View { @Binding var showingSubscriptionPaywall: Bool - @EnvironmentObject var subscriptionController: SubscriptionController @State private var price = "" @State private var alertTitle = "" @@ -66,7 +65,6 @@ Purchases.shared.purchasePackage(package) { (transaction, purchaserInfo, error, userCancelled) in if purchaserInfo?.entitlements["all"]?.isActive == true { showingPrice = true - subscriptionController.isActive = true showingSubscriptionPaywall = false }
--- a/Simoleon/Helpers/SubscriberInfo.swift Sun Jul 25 10:59:51 2021 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +0,0 @@ -// -// SubscriberInfo.swift -// Simoleon -// -// Created by Dennis Concepción Martín on 22/07/2021. -// - -import SwiftUI -import Purchases - -struct SubscriberInfo: View { - @State private var memberSince: Date? = nil - @State private var expiration: Date? = nil - @State private var latestPurchase: Date? = nil - @State private var showingAlert = false - @State private var alertTitle = "" - @State private var alertMessage = "" - - var body: some View { - VStack { - List { - if let memberSince = self.memberSince { - Text("Member since \(formatDate(memberSince))", comment: "Subscriber information") - } else { - Text("-") - } - - if let expiration = self.expiration { - Text("Expires at \(formatDate(expiration))", comment: "Subscriber information") - } else { - Text("-") - } - - if let latestPurchase = self.latestPurchase { - Text("Latest purchase \(formatDate(latestPurchase))", comment: "Subscriber information") - } else { - Text("-") - } - } - .listStyle(InsetGroupedListStyle()) - } - .navigationTitle(Text("Information", comment: "Navigation title")) - .onAppear(perform: getInfo) - .alert(isPresented: $showingAlert) { - Alert(title: Text(alertTitle), message: Text(alertMessage), dismissButton: .default(Text("Ok", comment: "Dismiss alert"))) - } - } - - private func getInfo() { - Purchases.shared.purchaserInfo { (purchaserInfo, error) in - self.memberSince = purchaserInfo?.entitlements["all"]?.originalPurchaseDate - self.expiration = purchaserInfo?.entitlements["all"]?.expirationDate - self.latestPurchase = purchaserInfo?.entitlements["all"]?.latestPurchaseDate - - if let error = error as NSError? { - alertTitle = error.localizedDescription - alertMessage = error.localizedFailureReason ?? "" - showingAlert = true - } - } - } - - private func formatDate(_ date: Date) -> String { - let formatter = DateFormatter() - formatter.dateStyle = .long - let dateString = formatter.string(from: date) - - return dateString - } -} - -struct SubscriberInfo_Previews: PreviewProvider { - static var previews: some View { - SubscriberInfo() - .environmentObject(SubscriptionController()) - } -}
--- a/Simoleon/Helpers/SubscriptionController.swift Sun Jul 25 10:59:51 2021 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -// -// SubscriptionController.swift -// Simoleon -// -// Created by Dennis Concepción Martín on 22/07/2021. -// - -import SwiftUI - -class SubscriptionController: ObservableObject { - @Published var isActive = false -}
--- a/Simoleon/Settings.swift Sun Jul 25 10:59:51 2021 +0100 +++ b/Simoleon/Settings.swift Mon Jul 26 15:35:06 2021 +0100 @@ -9,31 +9,19 @@ import Purchases struct Settings: View { - @EnvironmentObject var subscriptionController: SubscriptionController @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 let currencyPairs: [String] = parseJson("CurrencyPairs.json") var body: some View { List { - Section(header: Text("Subscription", comment: "Section header in settings")) { - if subscriptionController.isActive { - NavigationLink(destination: SubscriberInfo()) { - Text("Information", comment: "Button to show subscription information in settings") - } - } else { - Button(action: { showingSubscriptionPaywall = true }) { - Text("Subscribe", comment: "Button to suscribe in settings") - } - } - } - Section(header: Text("Preferences", comment: "Section header in settings")) { - if subscriptionController.isActive { + if entitlementIsActive { Picker(selection: $selectedDefaultCurrency, label: Text("Default currency", comment: "Picker to select default currency"), content: { ForEach(currencyPairs.sorted(), id: \.self) { currencyPair in Text(currencyPair) @@ -83,7 +71,7 @@ Text("Website", comment: "Button to go to Dennis Tech website") } - Link(destination: URL(string: "https://dennistech.io")!) { + Link(destination: URL(string: "https://dennistech.io/privacy-policy")!) { Text("Privacy Policy", comment: "Button to go to app privacy policy") } @@ -92,27 +80,30 @@ } } } - .onAppear(perform: onAppear) + .onAppear { + /* + 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 == "" { + self.selectedDefaultCurrency = defaultCurrency.first?.pair ?? "USD/GBP" + } else { + setCoreData() + } + } .listStyle(InsetGroupedListStyle()) .navigationTitle(Text("Settings", comment: "Navigation title")) - .sheet(isPresented: $showingSubscriptionPaywall) { - Subscription(showingSubscriptionPaywall: $showingSubscriptionPaywall) - .environmentObject(subscriptionController) + .sheet(isPresented: $showingSubscriptionPaywall, onDismiss: checkEntitlement) { + SubscriptionPaywall(showingSubscriptionPaywall: $showingSubscriptionPaywall) } .if(UIDevice.current.userInterfaceIdiom == .phone) { content in NavigationView { content } } } - private func onAppear() { - // Set initial value of the picker - if selectedDefaultCurrency == "" { - self.selectedDefaultCurrency = defaultCurrency.first?.pair ?? "USD/GBP" - } else { - setCoreData() - } - } - + /* + Save default currency to core data + */ private func setCoreData() { if self.defaultCurrency.isEmpty { // If it's empty -> add record let defaultCurrency = DefaultCurrency(context: viewContext) @@ -128,12 +119,26 @@ try? viewContext.save() } } + + /* + Check if user subscription is active + */ + private func checkEntitlement() { + Purchases.shared.purchaserInfo { (purchaserInfo, error) in + if purchaserInfo?.entitlements["all"]?.isActive == true { + entitlementIsActive = true + print("Entitlement is active") + } else { + entitlementIsActive = false + print("Entitlement is NOT active") + } + } + } } struct Settings_Previews: PreviewProvider { static var previews: some View { Settings() - .environmentObject(SubscriptionController()) .environment(\.locale, .init(identifier: "es")) } }
--- a/Simoleon/Subscription.swift Sun Jul 25 10:59:51 2021 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,116 +0,0 @@ -// -// Subscription.swift -// Simoleon -// -// Created by Dennis Concepción Martín on 22/07/2021. -// - -import SwiftUI - -struct Subscription: 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) - - Text("Unlock all access", comment: "Headline in Subscription paywall") - .font(.title) - .fontWeight(.semibold) - .padding(.top) - } - - Spacer() - } - - Divider() - - HStack(alignment:.top) { - Image(systemName: "star.circle.fill") - .foregroundColor(Color(.systemYellow)) - .font(.title) - - VStack(alignment: .leading) { - Text("Favourite currencies", comment: "Subscription feature title") - .font(.headline) - - Text("Save your favourite currencies to access them quickly.", comment: "Subscription feature description") - } - } - - HStack(alignment:.top) { - Image(systemName: "flag.circle.fill") - .foregroundColor(Color(.systemRed)) - .font(.title) - - VStack(alignment: .leading) { - Text("Over 170 currencies", comment: "Subscription feature title") - .font(.headline) - - Text("Have access to almost every currency of the world.", comment: "Subscription feature description") - } - } - - HStack(alignment:.top) { - Image(systemName: "icloud.circle.fill") - .foregroundColor(Color(.systemBlue)) - .font(.title) - - VStack(alignment: .leading) { - Text("Simoleon on all your devices", comment: "Subscription feature title") - .font(.headline) - - Text("Your settings and favourite currencies in all your devices.", comment: "Subscription feature description") - } - } - - HStack(alignment:.top) { - Image(systemName: "bitcoinsign.circle.fill") - .foregroundColor(Color(.systemOrange)) - .font(.title) - - VStack(alignment: .leading) { - Text("Cryptos and commodities", comment: "Subscription feature title") - .font(.headline) - - Text("Convert your currency between cryptos, gold, and silver.", comment: "Subscription feature description") - } - } - - Spacer() - SubscribeButton(showingSubscriptionPaywall: $showingSubscriptionPaywall) - HStack { - Spacer() - RestoreButton(showingSubscriptionPaywall: $showingSubscriptionPaywall) - Spacer() - } - - } - .padding(.bottom) - .padding(.horizontal, 40) - } - .toolbar { - ToolbarItem(placement: .cancellationAction) { - Button(action: { showingSubscriptionPaywall = false }) { - Text("Cancel", comment: "Button to dismiss paywall modal sheet") - } - } - } - } - } -} - -struct Subscription_Previews: PreviewProvider { - static var previews: some View { - Subscription(showingSubscriptionPaywall: .constant(false)) - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Simoleon/SubscriptionPaywall.swift Mon Jul 26 15:35:06 2021 +0100 @@ -0,0 +1,128 @@ +// +// 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) + + Text("Unlock all access", comment: "Headline in Subscription paywall") + .font(.title) + .fontWeight(.semibold) + .padding(.top) + } + + Spacer() + } + + Divider() + + HStack(alignment:.top) { + Image(systemName: "star.circle.fill") + .foregroundColor(Color(.systemYellow)) + .font(.title) + + VStack(alignment: .leading) { + Text("Favourite currencies", comment: "Subscription feature title") + .font(.headline) + + Text("Save your favourite currencies to access them quickly.", comment: "Subscription feature description") + } + } + + HStack(alignment:.top) { + Image(systemName: "flag.circle.fill") + .foregroundColor(Color(.systemRed)) + .font(.title) + + VStack(alignment: .leading) { + Text("Over 170 currencies", comment: "Subscription feature title") + .font(.headline) + + Text("Have access to almost every currency of the world.", comment: "Subscription feature description") + } + } + + HStack(alignment:.top) { + Image(systemName: "icloud.circle.fill") + .foregroundColor(Color(.systemBlue)) + .font(.title) + + VStack(alignment: .leading) { + Text("Simoleon on all your devices", comment: "Subscription feature title") + .font(.headline) + + Text("Your settings and favourite currencies in all your devices.", comment: "Subscription feature description") + } + } + + HStack(alignment:.top) { + Image(systemName: "bitcoinsign.circle.fill") + .foregroundColor(Color(.systemOrange)) + .font(.title) + + VStack(alignment: .leading) { + Text("Cryptos and commodities", comment: "Subscription feature title") + .font(.headline) + + Text("Convert your currency between cryptos, gold, and silver.", comment: "Subscription feature description") + } + } + + Spacer() + SubscribeButton(showingSubscriptionPaywall: $showingSubscriptionPaywall) + HStack { + Spacer() + VStack { + RestoreButton(showingSubscriptionPaywall: $showingSubscriptionPaywall) + .padding(.bottom) + HStack { + Link(destination: URL(string: "https://dennistech.io/privacy-policy")!) { + Text("Privacy Policy", comment: "Button to go to app privacy policy") + } + + Link(destination: URL(string: "https://dennistech.io/terms-of-use")!) { + Text("Terms of Use", comment: "Button to go to app terms of use") + } + } + } + + Spacer() + } + } + .padding(.bottom) + .padding(.horizontal, 40) + } + .toolbar { + ToolbarItem(placement: .cancellationAction) { + Button(action: { showingSubscriptionPaywall = false }) { + Text("Cancel", comment: "Button to dismiss paywall modal sheet") + } + } + } + } + } +} + +struct SubscriptionPaywall_Previews: PreviewProvider { + static var previews: some View { + SubscriptionPaywall(showingSubscriptionPaywall: .constant(false)) + } +}