From 92360d05dd07d13d3a9c2d5cde2b8659e863ccba Mon Sep 17 00:00:00 2001 From: Laurent Date: Thu, 6 Jun 2024 09:20:39 +0200 Subject: [PATCH] Improve compilation times --- PadelClub.xcodeproj/project.pbxproj | 8 +- PadelClub/Views/Components/StepperView.swift | 6 +- .../Toolbox/DebugSettingsView.swift | 37 +++- PadelClub/Views/Subscription/Guard.swift | 2 +- .../Views/Subscription/SubscriptionView.swift | 172 ++++++++++-------- .../ViewModifiers/LoadingViewModifier.swift | 34 ++++ 6 files changed, 170 insertions(+), 89 deletions(-) create mode 100644 PadelClub/Views/ViewModifiers/LoadingViewModifier.swift diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 37fb475..16988eb 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ C45BAE3B2BC6DF10002EEC8A /* SyncedProducts.storekit in Resources */ = {isa = PBXBuildFile; fileRef = C45BAE3A2BC6DF10002EEC8A /* SyncedProducts.storekit */; }; C45BAE442BCA753E002EEC8A /* Purchase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45BAE432BCA753E002EEC8A /* Purchase.swift */; }; C4607A7D2C04DDE2004CB781 /* APICallsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4607A7C2C04DDE2004CB781 /* APICallsListView.swift */; }; + C493B37E2C10AD3600862481 /* LoadingViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C493B37D2C10AD3600862481 /* LoadingViewModifier.swift */; }; C49EF0192BD694290077B5AA /* PurchaseListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0182BD694290077B5AA /* PurchaseListView.swift */; }; C49EF01B2BD6A1E80077B5AA /* URLs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF01A2BD6A1E80077B5AA /* URLs.swift */; }; C49EF0262BD80AE80077B5AA /* SubscriptionInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0252BD80AE80077B5AA /* SubscriptionInfoView.swift */; }; @@ -303,6 +304,7 @@ C45BAE3A2BC6DF10002EEC8A /* SyncedProducts.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = SyncedProducts.storekit; sourceTree = ""; }; C45BAE432BCA753E002EEC8A /* Purchase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Purchase.swift; sourceTree = ""; }; C4607A7C2C04DDE2004CB781 /* APICallsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APICallsListView.swift; sourceTree = ""; }; + C493B37D2C10AD3600862481 /* LoadingViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingViewModifier.swift; sourceTree = ""; }; C49EF0182BD694290077B5AA /* PurchaseListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PurchaseListView.swift; sourceTree = ""; }; C49EF01A2BD6A1E80077B5AA /* URLs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLs.swift; sourceTree = ""; }; C49EF0252BD80AE80077B5AA /* SubscriptionInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionInfoView.swift; sourceTree = ""; }; @@ -1228,6 +1230,7 @@ FFDDD40B2B93B2BB00C91A49 /* DeferredViewModifier.swift */, FF5D0D752BB428B2005CB568 /* ListRowViewModifier.swift */, FF4C7F012BBBD7150031B6A3 /* TabItemModifier.swift */, + C493B37D2C10AD3600862481 /* LoadingViewModifier.swift */, ); path = ViewModifiers; sourceTree = ""; @@ -1645,6 +1648,7 @@ FFF527D62BC6DDD000FF4EF2 /* MatchScheduleEditorView.swift in Sources */, FFC91AF92BD6A09100B29808 /* FortuneWheelView.swift in Sources */, FFF8ACD62B923960008466FA /* URL+Extensions.swift in Sources */, + C493B37E2C10AD3600862481 /* LoadingViewModifier.swift in Sources */, FF089EBD2BB0287D00F0AEC7 /* PlayerView.swift in Sources */, FF967D032BAEF0C000A9A3BD /* MatchDetailView.swift in Sources */, FF967D0F2BAF63B000A9A3BD /* PlayerBlockView.swift in Sources */, @@ -1878,7 +1882,7 @@ ); MARKETING_VERSION = 0.1; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - OTHER_SWIFT_FLAGS = ""; + OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-function-bodies=5 -Xfrontend -warn-long-expression-type-checking=20 -Xfrontend -warn-long-function-bodies=50"; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -1916,7 +1920,7 @@ ); MARKETING_VERSION = 0.1; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - OTHER_SWIFT_FLAGS = ""; + OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-function-bodies=5 -Xfrontend -warn-long-expression-type-checking=20 -Xfrontend -warn-long-function-bodies=50"; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; diff --git a/PadelClub/Views/Components/StepperView.swift b/PadelClub/Views/Components/StepperView.swift index 197c2b8..aac0bf7 100644 --- a/PadelClub/Views/Components/StepperView.swift +++ b/PadelClub/Views/Components/StepperView.swift @@ -7,8 +7,6 @@ import SwiftUI - - struct StepperView: View { var title: String? = nil @@ -16,6 +14,8 @@ struct StepperView: View { var step: Int = 1 var minimum: Int? = nil var maximum: Int? = nil + + var countChanged: (() -> ())? = nil var body: some View { VStack { @@ -75,6 +75,7 @@ struct StepperView: View { return } self.count += step + self.countChanged?() } fileprivate func _subtract() { @@ -82,6 +83,7 @@ struct StepperView: View { return } self.count -= step + self.countChanged?() } } diff --git a/PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift b/PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift index 30fcf8f..1258d24 100644 --- a/PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift +++ b/PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift @@ -11,15 +11,38 @@ import LeStorage struct DebugSettingsView: View { var body: some View { List { - LabeledContent("UUID", value: Store.main.userId ?? "") - LabeledContent("User Name", value: Store.main.userName() ?? "") - LabeledContent("Token", value: Store.main.token() ?? "") - LabeledContent("Server", value: PListReader.readString(plist: "local", key: "server") ?? "") - LabeledContent("Synchronized", value: - "\(PListReader.readBool(plist: "local", key: "synchronized"))") - LabeledContent("CollectionsCanSynchronize", value: "\(Store.main.collectionsCanSynchronize)") + LabeledContent("UUID", value: self._userId) + LabeledContent("User Name", value: self._userName) + LabeledContent("Token", value: self._token) + LabeledContent("Server", value: self._server) + LabeledContent("Synchronized", value: self._synchronized) + LabeledContent("CollectionsCanSynchronize", value: self._canSynchronize) } } + + fileprivate var _userId: String { + return Store.main.userId ?? "" + } + + fileprivate var _userName: String { + return Store.main.userName() ?? "" + } + + fileprivate var _token: String { + return Store.main.token() ?? "" + } + + fileprivate var _server: String { + return PListReader.readString(plist: "local", key: "server") ?? "" + } + + fileprivate var _synchronized: String { + return "\(PListReader.readBool(plist: "local", key: "synchronized"))" + } + + fileprivate var _canSynchronize: String { + return "\(Store.main.collectionsCanSynchronize)" + } } #Preview { diff --git a/PadelClub/Views/Subscription/Guard.swift b/PadelClub/Views/Subscription/Guard.swift index ca95431..c74974e 100644 --- a/PadelClub/Views/Subscription/Guard.swift +++ b/PadelClub/Views/Subscription/Guard.swift @@ -141,7 +141,7 @@ import LeStorage var currentPlan: StoreItem? { // #if DEBUG - return .monthlyUnlimited +// return .monthlyUnlimited // #else if let currentBestPlan = self.currentBestPlan, let plan = StoreItem(rawValue: currentBestPlan.productID) { return plan diff --git a/PadelClub/Views/Subscription/SubscriptionView.swift b/PadelClub/Views/Subscription/SubscriptionView.swift index 6736a7e..37b8ece 100644 --- a/PadelClub/Views/Subscription/SubscriptionView.swift +++ b/PadelClub/Views/Subscription/SubscriptionView.swift @@ -43,12 +43,7 @@ class SubscriptionModel: ObservableObject, StoreDelegate { self._computePrice() } } - @Published var quantity: Int = 1 { - didSet { - self._computePrice() - self.selectedProduct = self.products.first(where: { $0.id == StoreItem.unit.rawValue }) - } - } + @Published var quantity: Int = 1 @Published var products: [Product] = [] @Published var totalPrice: String = "" @@ -63,6 +58,10 @@ class SubscriptionModel: ObservableObject, StoreDelegate { } } + func isSelected(product: Product) -> Bool { + return self.selectedProduct == product + } + func productsReceived(products: [Product]) { self.isLoading = false self.products = products @@ -137,60 +136,22 @@ struct SubscriptionView: View { if self.showLackOfPlanMessage { SubscriptionDetailView() - .clipShape(.rect(cornerRadius: 16.0)) - .padding() } List { if self.model.products.count > 0 { - Section { - - ForEach(self.model.products) { product in - let isSelected = self.model.selectedProduct == product - ProductView(product: product, - quantity: self.$model.quantity, - selected: isSelected) - .foregroundStyle(.white) - .frame(maxWidth: .infinity) - .buttonStyle(.borderedProminent) - .tint(Color.master) - .listRowBackground(Color.clear) - - .onTapGesture { - self.model.selectedProduct = product - } - } - } header: { - Text("Sélectionnez une offre") - } + ProductsSectionView(model: self.model) if let product = self.model.selectedProduct { Section { Button { - if Store.main.userId != nil { - self._purchase() - } else { - self.showLoginView = true - } + self._purchaseIfPossible() } label: { - HStack { - if self.isPurchasing { - Spacer() - ProgressView().tint(.white) - Spacer() - } else { - Text("Acheter") - Spacer() - Text(self.model.totalPrice) - } - } - .padding(8.0) - .fontWeight(.bold) - + PurchaseLabelView(price: self.model.totalPrice, isPurchasing: self.isPurchasing) } .buttonStyle(.borderedProminent) .tint(.orange) @@ -205,19 +166,8 @@ struct SubscriptionView: View { } } else { - if self.model.isLoading { - ProgressView() - } else { - HStack { - if let plan = Guard.main.currentPlan { - Image(systemName: plan.systemImage) - } else { - Image(systemName: "questionmark.diamond.fill") - } - Text("Il n'y a pas de produits à vous proposer") - } - } - + NoProductView() + .isLoading(self.model.isLoading) } } .listStyle(.grouped) @@ -225,13 +175,9 @@ struct SubscriptionView: View { .background(.logoBackground) .toolbar { ToolbarItem(placement: .navigationBarTrailing) { - if self.isRestoring { - ProgressView() - } else { - Button("Restaurer") { - self._restore() - } - } + Button("Restaurer") { + self._restore() + }.isLoading(self.isRestoring) } } } @@ -241,6 +187,14 @@ struct SubscriptionView: View { } + fileprivate func _purchaseIfPossible() { + if Store.main.userId != nil { + self._purchase() + } else { + self.showLoginView = true + } + } + fileprivate func _purchase() { self.isPurchasing = true @@ -280,11 +234,69 @@ struct SubscriptionView: View { } +fileprivate struct ProductsSectionView: View { + + @ObservedObject var model: SubscriptionModel + + var body: some View { + Section { + ForEach(self.model.products) { product in + ProductView(product: product, + model: self.model) + .onTapGesture { + self.model.selectedProduct = product + } + } + } header: { + Text("Sélectionnez une offre") + } + + } +} + +fileprivate struct PurchaseLabelView: View { + + var price: String + var isPurchasing: Bool + + var body: some View { + HStack(alignment: .center) { + if self.isPurchasing { + Spacer() + ProgressView().tint(Color.white) + Spacer() + } else { + Text("Acheter") + Spacer() + Text(self.price) + } + } + .padding(8.0) + .fontWeight(.bold) + } +} + +fileprivate struct NoProductView: View { + var body: some View { + HStack { + if let plan = Guard.main.currentPlan { + Image(systemName: plan.systemImage) + } else { + Image(systemName: "questionmark.diamond.fill") + } + Text("Il n'y a pas de produits à vous proposer") + } + } +} + struct ProductView: View { var product: Product - @Binding var quantity: Int - var selected: Bool + + @ObservedObject var model: SubscriptionModel + +// @Binding var quantity: Int +// var selected: Bool var body: some View { HStack { @@ -292,22 +304,26 @@ struct ProductView: View { .font(.system(size: 36.0)) .foregroundColor(.orange) VStack(alignment: .leading) { - Text(product.displayName) - Text(product.formattedPrice) + Text(self.product.displayName) + Text(self.product.formattedPrice) .foregroundColor(.accentColor) if self._isConsumable { - StepperView(count: self.$quantity, minimum: 1) + StepperView(count: self.$model.quantity, minimum: 1, countChanged: { self.model.selectedProduct = self.product }) .font(.callout).foregroundColor(.accentColor) } } Spacer() - if self.selected { + if self.model.isSelected(product: self.product) { Image(systemName: "checkmark") .foregroundColor(.accentColor) } } .contentShape(.rect) - + .foregroundStyle(.white) + .frame(maxWidth: .infinity) + .buttonStyle(.borderedProminent) + .tint(Color.master) + .listRowBackground(Color.clear) } fileprivate var _item: StoreItem? { @@ -328,7 +344,7 @@ struct ProductView: View { } -struct SubscriptionNoProductView: View { +fileprivate struct SubscriptionNoProductView: View { @ObservedObject var model: SubscriptionModel @@ -356,14 +372,14 @@ struct SubscriptionNoProductView: View { } } -struct SubscriptionFooterView: View { +fileprivate struct SubscriptionFooterView: View { var body: some View { Text("Conditions d’utilisations concernant l’abonnement:\n- Le paiement sera facturé sur votre compte Apple.\n- L’abonnement est renouvelé automatiquement chaque mois, à moins d’avoir été désactivé au moins 24 heures avant la fin de la période de l’abonnement.\n- L’abonnement peut être géré par l’utilisateur et désactivé en allant dans les réglages de son compte après s’être abonné.\n- Le compte sera facturé pour le renouvellement de l'abonnement dans les 24 heures précédent la fin de la période d’abonnement.\n- Un abonnement en cours ne peut être annulé.\n- Toute partie inutilisée de l'offre gratuite, si souscrite, sera abandonnée lorsque l'utilisateur s'abonnera, dans les cas applicables.") .padding(.top) } } -struct SubscriptionDetailView: View { +fileprivate struct SubscriptionDetailView: View { var body: some View { HStack { @@ -376,6 +392,8 @@ struct SubscriptionDetailView: View { .padding() .background(.orange) .foregroundStyle(.black) + .clipShape(.rect(cornerRadius: 16.0)) + .padding() } } diff --git a/PadelClub/Views/ViewModifiers/LoadingViewModifier.swift b/PadelClub/Views/ViewModifiers/LoadingViewModifier.swift new file mode 100644 index 0000000..cbe60fe --- /dev/null +++ b/PadelClub/Views/ViewModifiers/LoadingViewModifier.swift @@ -0,0 +1,34 @@ +// +// LoadingViewModifier.swift +// PadelClub +// +// Created by Laurent Morvillier on 05/06/2024. +// + +import Foundation +import SwiftUI + +struct LoadingViewModifier: ViewModifier { + + let isLoading: Bool + let tintColor: Color? + + func body(content: Content) -> some View { + if self.isLoading { + if let tintColor { + ProgressView().tint(tintColor) + } else { + ProgressView() + } + } else { + content + } + } + +} + +extension View { + func isLoading(_ isLoading: Bool, tintColor: Color? = nil) -> some View { + modifier(LoadingViewModifier(isLoading: isLoading, tintColor: tintColor)) + } +}