diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index e9ed9ba..bb87f8d 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -23,6 +23,9 @@ 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 */; }; + C49C731E2D5E3BE8008DD299 /* VersionComparator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49C731D2D5E3BE4008DD299 /* VersionComparator.swift */; }; + C49C731F2D5E3BE8008DD299 /* VersionComparator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49C731D2D5E3BE4008DD299 /* VersionComparator.swift */; }; + C49C73202D5E3BE8008DD299 /* VersionComparator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49C731D2D5E3BE4008DD299 /* VersionComparator.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 */; }; @@ -942,6 +945,7 @@ 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 = ""; }; + C49C731D2D5E3BE4008DD299 /* VersionComparator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionComparator.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 = ""; }; @@ -2030,6 +2034,7 @@ FF0EC51D2BB16F680056B6D1 /* SwiftParser.swift */, FF1DC5582BAB767000FD8220 /* Tips.swift */, C49EF01A2BD6A1E80077B5AA /* URLs.swift */, + C49C731D2D5E3BE4008DD299 /* VersionComparator.swift */, FFF1D2CA2C4A22B200C8D33D /* ExportFormat.swift */, ); path = Utils; @@ -2393,6 +2398,7 @@ FF1DC5552BAB36DD00FD8220 /* CreateClubView.swift in Sources */, C4607A7D2C04DDE2004CB781 /* APICallsListView.swift in Sources */, FF7DCD3B2CC330270041110C /* TeamRestingView.swift in Sources */, + C49C731F2D5E3BE8008DD299 /* VersionComparator.swift in Sources */, FFC1E10A2BAC2A77008D6F59 /* NetworkFederalService.swift in Sources */, FF025AEF2BD1AE9400A86CF8 /* DurationSettingsView.swift in Sources */, FF025AED2BD1513700A86CF8 /* AppScreen.swift in Sources */, @@ -2683,6 +2689,7 @@ FF4CBF812C996C0600151637 /* CreateClubView.swift in Sources */, FF4CBF822C996C0600151637 /* APICallsListView.swift in Sources */, FF7DCD392CC330270041110C /* TeamRestingView.swift in Sources */, + C49C731E2D5E3BE8008DD299 /* VersionComparator.swift in Sources */, FF4CBF832C996C0600151637 /* NetworkFederalService.swift in Sources */, FF4CBF842C996C0600151637 /* DurationSettingsView.swift in Sources */, FF4CBF852C996C0600151637 /* AppScreen.swift in Sources */, @@ -2952,6 +2959,7 @@ FF70FB002C90584900129CC2 /* CreateClubView.swift in Sources */, FF70FB012C90584900129CC2 /* APICallsListView.swift in Sources */, FF7DCD3A2CC330270041110C /* TeamRestingView.swift in Sources */, + C49C73202D5E3BE8008DD299 /* VersionComparator.swift in Sources */, FF70FB022C90584900129CC2 /* NetworkFederalService.swift in Sources */, FF70FB032C90584900129CC2 /* DurationSettingsView.swift in Sources */, FF70FB042C90584900129CC2 /* AppScreen.swift in Sources */, diff --git a/PadelClub/PadelClubApp.swift b/PadelClub/PadelClubApp.swift index 20c2389..b718dbc 100644 --- a/PadelClub/PadelClubApp.swift +++ b/PadelClub/PadelClubApp.swift @@ -19,6 +19,8 @@ struct PadelClubApp: App { @State private var importObserverViewModel = ImportObserver() @Environment(\.horizontalSizeClass) var horizontalSizeClass + @State var blockApp = false + @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var presentError: Binding { @@ -62,54 +64,79 @@ struct PadelClubApp: App { var body: some Scene { WindowGroup { - MainView() - .environment(\.horizontalSizeClass, .compact) - .alert(isPresented: presentError, error: registrationError) { - Button("Contactez-nous") { - _openMail() - } - Button("Annuler", role: .cancel) { - registrationError = nil + + if self.blockApp { + DownloadNewVersionView() + } else { + MainView() + .environment(\.horizontalSizeClass, .compact) + .alert(isPresented: presentError, error: registrationError) { + Button("Contactez-nous") { + _openMail() + } + Button("Annuler", role: .cancel) { + registrationError = nil + } } - } - .onOpenURL { url in + .onOpenURL { url in #if targetEnvironment(simulator) #else - _handleIncomingURL(url) + _handleIncomingURL(url) #endif - } - .environmentObject(networkMonitor) - .environmentObject(dataStore) - .environment(importObserverViewModel) - .environment(navigationViewModel) - .accentColor(.master) - .onAppear { + } + .environmentObject(networkMonitor) + .environmentObject(dataStore) + .environment(importObserverViewModel) + .environment(navigationViewModel) + .accentColor(.master) + .onAppear { + self._checkVersion() #if DEBUG -print("Running in Debug mode") + print("Running in Debug mode") #elseif TESTFLIGHT -print("Running in TestFlight mode") + print("Running in TestFlight mode") #elseif PRODTEST -print("Running in ProdTest mode") + print("Running in ProdTest mode") #else -print("Running in Release mode") + print("Running in Release mode") #endif - networkMonitor.checkConnection() - self._onAppear() - print(PersistenceController.getModelVersion()) - } - .task { - - try? Tips.resetDatastore() - - try? Tips.configure([ - .displayFrequency(.immediate), - .datastoreLocation(.applicationDefault) - ]) + networkMonitor.checkConnection() + self._onAppear() + print(PersistenceController.getModelVersion()) + } + .task { + + try? Tips.resetDatastore() + + try? Tips.configure([ + .displayFrequency(.immediate), + .datastoreLocation(.applicationDefault) + ]) + } + .environment(\.managedObjectContext, persistenceController.localContainer.viewContext) + } + } + } + + fileprivate func _checkVersion() { + Task.detached(priority: .high) { + if let requiredVersion = await self._retrieveRequiredVersion() { + let cleanedRequired = requiredVersion.replacingOccurrences(of: "\n", with: "") + Logger.log(">>> VERSION = \(requiredVersion)") + if let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String { + await MainActor.run { + self.blockApp = VersionComparator.compare(cleanedRequired, currentVersion) == 1 + } } - .environment(\.managedObjectContext, persistenceController.localContainer.viewContext) + } } } + fileprivate func _retrieveRequiredVersion() async -> String? { + let requiredVersionURL = URLs.main.extend(path: "static/misc/required-version.txt") + return try? String(contentsOf: requiredVersionURL, encoding: .utf8) + } + private func _handleIncomingURL(_ url: URL) { // Parse the URL let pathComponents = url.pathComponents @@ -173,3 +200,31 @@ print("Running in Release mode") } } } + + +struct DownloadNewVersionView: View { + + var body: some View { + + VStack { +// AngledStripesBackground() + Spacer() + Text("Veuillez télécharger la nouvelle version de Padel Club pour continuer à vous servir de l'app !") + .padding(32.0) + .background(.logoYellow) + .clipShape(.buttonBorder) + .foregroundStyle(.logoBackground) + .fontWeight(.medium) + .multilineTextAlignment(.center) + .padding(.horizontal, 64.0) + Image("logo").padding(.vertical, 50.0) + Spacer() + }.background(.logoBackground) + .onTapGesture { + UIApplication.shared.open(URLs.appStore.url) + } + + } + +} + diff --git a/PadelClub/Utils/URLs.swift b/PadelClub/Utils/URLs.swift index 63f4489..2b9e746 100644 --- a/PadelClub/Utils/URLs.swift +++ b/PadelClub/Utils/URLs.swift @@ -46,7 +46,12 @@ enum URLs: String, Identifiable { var url: URL { return URL(string: self.rawValue)! - } + } + + func extend(path: String) -> URL { + return URL(string: self.rawValue + path)! + } + } enum PageLink: String, Identifiable, CaseIterable { diff --git a/PadelClub/Utils/VersionComparator.swift b/PadelClub/Utils/VersionComparator.swift new file mode 100644 index 0000000..9c9a240 --- /dev/null +++ b/PadelClub/Utils/VersionComparator.swift @@ -0,0 +1,33 @@ +// +// VersionComparator.swift +// PadelClub +// +// Created by Laurent Morvillier on 13/02/2025. +// + +class VersionComparator { + + static func compare(_ version1: String, _ version2: String) -> Int { + // Split versions into components + let v1Components = version1.split(separator: ".").map { Int($0) ?? 0 } + let v2Components = version2.split(separator: ".").map { Int($0) ?? 0 } + + // Get the maximum length to compare + let maxLength = max(v1Components.count, v2Components.count) + + // Compare each component + for i in 0.. v2Num { + return 1 // version1 is larger + } + } + + return 0 // versions are equal + } + +}