Improve compilation times

multistore
Laurent 1 year ago
parent 8116c0598a
commit 92360d05dd
  1. 8
      PadelClub.xcodeproj/project.pbxproj
  2. 6
      PadelClub/Views/Components/StepperView.swift
  3. 37
      PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift
  4. 2
      PadelClub/Views/Subscription/Guard.swift
  5. 172
      PadelClub/Views/Subscription/SubscriptionView.swift
  6. 34
      PadelClub/Views/ViewModifiers/LoadingViewModifier.swift

@ -21,6 +21,7 @@
C45BAE3B2BC6DF10002EEC8A /* SyncedProducts.storekit in Resources */ = {isa = PBXBuildFile; fileRef = C45BAE3A2BC6DF10002EEC8A /* SyncedProducts.storekit */; }; C45BAE3B2BC6DF10002EEC8A /* SyncedProducts.storekit in Resources */ = {isa = PBXBuildFile; fileRef = C45BAE3A2BC6DF10002EEC8A /* SyncedProducts.storekit */; };
C45BAE442BCA753E002EEC8A /* Purchase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45BAE432BCA753E002EEC8A /* Purchase.swift */; }; C45BAE442BCA753E002EEC8A /* Purchase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45BAE432BCA753E002EEC8A /* Purchase.swift */; };
C4607A7D2C04DDE2004CB781 /* APICallsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4607A7C2C04DDE2004CB781 /* APICallsListView.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 */; }; C49EF0192BD694290077B5AA /* PurchaseListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0182BD694290077B5AA /* PurchaseListView.swift */; };
C49EF01B2BD6A1E80077B5AA /* URLs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF01A2BD6A1E80077B5AA /* URLs.swift */; }; C49EF01B2BD6A1E80077B5AA /* URLs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF01A2BD6A1E80077B5AA /* URLs.swift */; };
C49EF0262BD80AE80077B5AA /* SubscriptionInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0252BD80AE80077B5AA /* SubscriptionInfoView.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 = "<group>"; }; C45BAE3A2BC6DF10002EEC8A /* SyncedProducts.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = SyncedProducts.storekit; sourceTree = "<group>"; };
C45BAE432BCA753E002EEC8A /* Purchase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Purchase.swift; sourceTree = "<group>"; }; C45BAE432BCA753E002EEC8A /* Purchase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Purchase.swift; sourceTree = "<group>"; };
C4607A7C2C04DDE2004CB781 /* APICallsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APICallsListView.swift; sourceTree = "<group>"; }; C4607A7C2C04DDE2004CB781 /* APICallsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APICallsListView.swift; sourceTree = "<group>"; };
C493B37D2C10AD3600862481 /* LoadingViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingViewModifier.swift; sourceTree = "<group>"; };
C49EF0182BD694290077B5AA /* PurchaseListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PurchaseListView.swift; sourceTree = "<group>"; }; C49EF0182BD694290077B5AA /* PurchaseListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PurchaseListView.swift; sourceTree = "<group>"; };
C49EF01A2BD6A1E80077B5AA /* URLs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLs.swift; sourceTree = "<group>"; }; C49EF01A2BD6A1E80077B5AA /* URLs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLs.swift; sourceTree = "<group>"; };
C49EF0252BD80AE80077B5AA /* SubscriptionInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionInfoView.swift; sourceTree = "<group>"; }; C49EF0252BD80AE80077B5AA /* SubscriptionInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionInfoView.swift; sourceTree = "<group>"; };
@ -1228,6 +1230,7 @@
FFDDD40B2B93B2BB00C91A49 /* DeferredViewModifier.swift */, FFDDD40B2B93B2BB00C91A49 /* DeferredViewModifier.swift */,
FF5D0D752BB428B2005CB568 /* ListRowViewModifier.swift */, FF5D0D752BB428B2005CB568 /* ListRowViewModifier.swift */,
FF4C7F012BBBD7150031B6A3 /* TabItemModifier.swift */, FF4C7F012BBBD7150031B6A3 /* TabItemModifier.swift */,
C493B37D2C10AD3600862481 /* LoadingViewModifier.swift */,
); );
path = ViewModifiers; path = ViewModifiers;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1645,6 +1648,7 @@
FFF527D62BC6DDD000FF4EF2 /* MatchScheduleEditorView.swift in Sources */, FFF527D62BC6DDD000FF4EF2 /* MatchScheduleEditorView.swift in Sources */,
FFC91AF92BD6A09100B29808 /* FortuneWheelView.swift in Sources */, FFC91AF92BD6A09100B29808 /* FortuneWheelView.swift in Sources */,
FFF8ACD62B923960008466FA /* URL+Extensions.swift in Sources */, FFF8ACD62B923960008466FA /* URL+Extensions.swift in Sources */,
C493B37E2C10AD3600862481 /* LoadingViewModifier.swift in Sources */,
FF089EBD2BB0287D00F0AEC7 /* PlayerView.swift in Sources */, FF089EBD2BB0287D00F0AEC7 /* PlayerView.swift in Sources */,
FF967D032BAEF0C000A9A3BD /* MatchDetailView.swift in Sources */, FF967D032BAEF0C000A9A3BD /* MatchDetailView.swift in Sources */,
FF967D0F2BAF63B000A9A3BD /* PlayerBlockView.swift in Sources */, FF967D0F2BAF63B000A9A3BD /* PlayerBlockView.swift in Sources */,
@ -1878,7 +1882,7 @@
); );
MARKETING_VERSION = 0.1; MARKETING_VERSION = 0.1;
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; 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_BUNDLE_IDENTIFIER = app.padelclub;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
@ -1916,7 +1920,7 @@
); );
MARKETING_VERSION = 0.1; MARKETING_VERSION = 0.1;
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; 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_BUNDLE_IDENTIFIER = app.padelclub;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;

@ -7,8 +7,6 @@
import SwiftUI import SwiftUI
struct StepperView: View { struct StepperView: View {
var title: String? = nil var title: String? = nil
@ -17,6 +15,8 @@ struct StepperView: View {
var minimum: Int? = nil var minimum: Int? = nil
var maximum: Int? = nil var maximum: Int? = nil
var countChanged: (() -> ())? = nil
var body: some View { var body: some View {
VStack { VStack {
HStack(spacing: 8) { HStack(spacing: 8) {
@ -75,6 +75,7 @@ struct StepperView: View {
return return
} }
self.count += step self.count += step
self.countChanged?()
} }
fileprivate func _subtract() { fileprivate func _subtract() {
@ -82,6 +83,7 @@ struct StepperView: View {
return return
} }
self.count -= step self.count -= step
self.countChanged?()
} }
} }

@ -11,15 +11,38 @@ import LeStorage
struct DebugSettingsView: View { struct DebugSettingsView: View {
var body: some View { var body: some View {
List { List {
LabeledContent("UUID", value: Store.main.userId ?? "") LabeledContent("UUID", value: self._userId)
LabeledContent("User Name", value: Store.main.userName() ?? "") LabeledContent("User Name", value: self._userName)
LabeledContent("Token", value: Store.main.token() ?? "") LabeledContent("Token", value: self._token)
LabeledContent("Server", value: PListReader.readString(plist: "local", key: "server") ?? "") LabeledContent("Server", value: self._server)
LabeledContent("Synchronized", value: LabeledContent("Synchronized", value: self._synchronized)
"\(PListReader.readBool(plist: "local", key: "synchronized"))") LabeledContent("CollectionsCanSynchronize", value: self._canSynchronize)
LabeledContent("CollectionsCanSynchronize", value: "\(Store.main.collectionsCanSynchronize)")
} }
} }
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 { #Preview {

@ -141,7 +141,7 @@ import LeStorage
var currentPlan: StoreItem? { var currentPlan: StoreItem? {
// #if DEBUG // #if DEBUG
return .monthlyUnlimited // return .monthlyUnlimited
// #else // #else
if let currentBestPlan = self.currentBestPlan, let plan = StoreItem(rawValue: currentBestPlan.productID) { if let currentBestPlan = self.currentBestPlan, let plan = StoreItem(rawValue: currentBestPlan.productID) {
return plan return plan

@ -43,12 +43,7 @@ class SubscriptionModel: ObservableObject, StoreDelegate {
self._computePrice() self._computePrice()
} }
} }
@Published var quantity: Int = 1 { @Published var quantity: Int = 1
didSet {
self._computePrice()
self.selectedProduct = self.products.first(where: { $0.id == StoreItem.unit.rawValue })
}
}
@Published var products: [Product] = [] @Published var products: [Product] = []
@Published var totalPrice: String = "" @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]) { func productsReceived(products: [Product]) {
self.isLoading = false self.isLoading = false
self.products = products self.products = products
@ -137,60 +136,22 @@ struct SubscriptionView: View {
if self.showLackOfPlanMessage { if self.showLackOfPlanMessage {
SubscriptionDetailView() SubscriptionDetailView()
.clipShape(.rect(cornerRadius: 16.0))
.padding()
} }
List { List {
if self.model.products.count > 0 { if self.model.products.count > 0 {
Section { ProductsSectionView(model: self.model)
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")
}
if let product = self.model.selectedProduct { if let product = self.model.selectedProduct {
Section { Section {
Button { Button {
if Store.main.userId != nil { self._purchaseIfPossible()
self._purchase()
} else {
self.showLoginView = true
}
} label: { } label: {
HStack { PurchaseLabelView(price: self.model.totalPrice, isPurchasing: self.isPurchasing)
if self.isPurchasing {
Spacer()
ProgressView().tint(.white)
Spacer()
} else {
Text("Acheter")
Spacer()
Text(self.model.totalPrice)
}
}
.padding(8.0)
.fontWeight(.bold)
} }
.buttonStyle(.borderedProminent) .buttonStyle(.borderedProminent)
.tint(.orange) .tint(.orange)
@ -205,19 +166,8 @@ struct SubscriptionView: View {
} }
} else { } else {
if self.model.isLoading { NoProductView()
ProgressView() .isLoading(self.model.isLoading)
} 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")
}
}
} }
} }
.listStyle(.grouped) .listStyle(.grouped)
@ -225,13 +175,9 @@ struct SubscriptionView: View {
.background(.logoBackground) .background(.logoBackground)
.toolbar { .toolbar {
ToolbarItem(placement: .navigationBarTrailing) { ToolbarItem(placement: .navigationBarTrailing) {
if self.isRestoring { Button("Restaurer") {
ProgressView() self._restore()
} else { }.isLoading(self.isRestoring)
Button("Restaurer") {
self._restore()
}
}
} }
} }
} }
@ -241,6 +187,14 @@ struct SubscriptionView: View {
} }
fileprivate func _purchaseIfPossible() {
if Store.main.userId != nil {
self._purchase()
} else {
self.showLoginView = true
}
}
fileprivate func _purchase() { fileprivate func _purchase() {
self.isPurchasing = true 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 { struct ProductView: View {
var product: Product 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 { var body: some View {
HStack { HStack {
@ -292,22 +304,26 @@ struct ProductView: View {
.font(.system(size: 36.0)) .font(.system(size: 36.0))
.foregroundColor(.orange) .foregroundColor(.orange)
VStack(alignment: .leading) { VStack(alignment: .leading) {
Text(product.displayName) Text(self.product.displayName)
Text(product.formattedPrice) Text(self.product.formattedPrice)
.foregroundColor(.accentColor) .foregroundColor(.accentColor)
if self._isConsumable { 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) .font(.callout).foregroundColor(.accentColor)
} }
} }
Spacer() Spacer()
if self.selected { if self.model.isSelected(product: self.product) {
Image(systemName: "checkmark") Image(systemName: "checkmark")
.foregroundColor(.accentColor) .foregroundColor(.accentColor)
} }
} }
.contentShape(.rect) .contentShape(.rect)
.foregroundStyle(.white)
.frame(maxWidth: .infinity)
.buttonStyle(.borderedProminent)
.tint(Color.master)
.listRowBackground(Color.clear)
} }
fileprivate var _item: StoreItem? { fileprivate var _item: StoreItem? {
@ -328,7 +344,7 @@ struct ProductView: View {
} }
struct SubscriptionNoProductView: View { fileprivate struct SubscriptionNoProductView: View {
@ObservedObject var model: SubscriptionModel @ObservedObject var model: SubscriptionModel
@ -356,14 +372,14 @@ struct SubscriptionNoProductView: View {
} }
} }
struct SubscriptionFooterView: View { fileprivate struct SubscriptionFooterView: View {
var body: some 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.") 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) .padding(.top)
} }
} }
struct SubscriptionDetailView: View { fileprivate struct SubscriptionDetailView: View {
var body: some View { var body: some View {
HStack { HStack {
@ -376,6 +392,8 @@ struct SubscriptionDetailView: View {
.padding() .padding()
.background(.orange) .background(.orange)
.foregroundStyle(.black) .foregroundStyle(.black)
.clipShape(.rect(cornerRadius: 16.0))
.padding()
} }
} }

@ -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))
}
}
Loading…
Cancel
Save