From aee0dda76a5dc3c05dc6752836844a443130f32f Mon Sep 17 00:00:00 2001 From: Alin Date: Wed, 12 Jun 2024 12:57:57 -0600 Subject: [PATCH] v3.7.1 --- Pearcleaner.xcodeproj/project.pbxproj | 8 ++-- Pearcleaner/Logic/Updater.swift | 58 ++++++++++++++++++++++++- Pearcleaner/Logic/Utilities.swift | 8 ++++ Pearcleaner/PearcleanerApp.swift | 27 ++++++------ Pearcleaner/Settings/Update.swift | 62 +++++++++++++++++++++++---- 5 files changed, 135 insertions(+), 28 deletions(-) diff --git a/Pearcleaner.xcodeproj/project.pbxproj b/Pearcleaner.xcodeproj/project.pbxproj index d8b6c05..4eb000c 100644 --- a/Pearcleaner.xcodeproj/project.pbxproj +++ b/Pearcleaner.xcodeproj/project.pbxproj @@ -568,8 +568,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BUILD = 45; - APP_VERSION = 3.7.0; + APP_BUILD = 46; + APP_VERSION = 3.7.1; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -638,8 +638,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BUILD = 45; - APP_VERSION = 3.7.0; + APP_BUILD = 46; + APP_VERSION = 3.7.1; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; diff --git a/Pearcleaner/Logic/Updater.swift b/Pearcleaner/Logic/Updater.swift index 11dc073..56b8a08 100644 --- a/Pearcleaner/Logic/Updater.swift +++ b/Pearcleaner/Logic/Updater.swift @@ -29,9 +29,10 @@ extension Release { } +// --- Updater functionality -func loadGithubReleases(appState: AppState, manual: Bool = false) { +func loadGithubReleases(appState: AppState, manual: Bool = false, releaseOnly: Bool = false) { let url = URL(string: "https://api.github.com/repos/alienator88/Pearcleaner/releases")! let request = URLRequest(url: url) URLSession.shared.dataTask(with: request) { data, response, error in @@ -40,7 +41,9 @@ func loadGithubReleases(appState: AppState, manual: Bool = false) { DispatchQueue.main.async { let lastFewReleases = Array(decodedResponse.prefix(3)) // Get only the last 3 recent releases appState.releases = lastFewReleases - checkForUpdate(appState: appState, manual: manual) + if !releaseOnly { + checkForUpdate(appState: appState, manual: manual) + } } return } @@ -165,7 +168,58 @@ func UnzipAndReplace(DownloadedFileURL fileURL: String, appState: AppState) { +// --- Updater check frequency +func updateNextUpdateDate() { + @AppStorage("settings.updater.updateTimeframe") var updateTimeframe: Int = 1 + @AppStorage("settings.updater.nextUpdateDate") var nextUpdateDate = Date.now.timeIntervalSinceReferenceDate + let updateSeconds = updateTimeframe.daysToSeconds + let newUpdateDate = Calendar.current.startOfDay(for: Date().addingTimeInterval(updateSeconds)) + nextUpdateDate = newUpdateDate.timeIntervalSinceReferenceDate +} + +func checkAndUpdateIfNeeded(appState: AppState) { + @AppStorage("settings.updater.updateTimeframe") var updateTimeframe: Int = 1 + @AppStorage("settings.updater.nextUpdateDate") var nextUpdateDate = Date.now.timeIntervalSinceReferenceDate + + let updateSeconds = updateTimeframe.daysToSeconds + let now = Date() + + // Retrieve the next update date from UserDefaults + let nextUpdateDateLocal = Date(timeIntervalSinceReferenceDate: nextUpdateDate) +// let nextUpdateDate = UserDefaults.standard.object(forKey: "settings.updater.nextUpdateDate") as? Date + + // If there's no stored next update date or it's in the past, update immediately + if !isSameDay(date1: nextUpdateDateLocal, date2: now) { + // Next update date is in the future, no need to update + printOS("Updater: next update date is in the future, skipping") + return + } + + // Update immediately and set next update date + updateApp(appState: appState) + setNextUpdateDate(interval: updateSeconds) +} + +func updateApp(appState: AppState) { + // Perform your update logic here + printOS("Updater: performing update") + loadGithubReleases(appState: appState) +} + +func setNextUpdateDate(interval: TimeInterval) { + let newUpdateDate = Calendar.current.startOfDay(for: Date().addingTimeInterval(interval)) + UserDefaults.standard.set(newUpdateDate.timeIntervalSinceReferenceDate, forKey: "settings.updater.nextUpdateDate") +// UserDefaults.standard.set(newUpdateDate, forKey: "settings.updater.nextUpdateDate") +} + +func isSameDay(date1: Date, date2: Date) -> Bool { + return Calendar.current.isDate(date1, inSameDayAs: date2) +} + + + +// --- Updater Badge View struct UpdateNotificationView: View { diff --git a/Pearcleaner/Logic/Utilities.swift b/Pearcleaner/Logic/Utilities.swift index 564ee98..200fff6 100644 --- a/Pearcleaner/Logic/Utilities.swift +++ b/Pearcleaner/Logic/Utilities.swift @@ -657,5 +657,13 @@ func getCurrentTimestamp() -> String { } +func formattedDate(_ date: Date?) -> String { + guard let date = date else { return "N/A" } + let formatter = DateFormatter() + formatter.dateStyle = .short + formatter.timeStyle = .none + return formatter.string(from: date) +} + diff --git a/Pearcleaner/PearcleanerApp.swift b/Pearcleaner/PearcleanerApp.swift index 38eb3fe..1e780fb 100644 --- a/Pearcleaner/PearcleanerApp.swift +++ b/Pearcleaner/PearcleanerApp.swift @@ -15,7 +15,8 @@ struct PearcleanerApp: App { @StateObject var locations = Locations() @StateObject var fsm = FolderSettingsManager() @State private var windowSettings = WindowSettings() - @AppStorage("settings.updater.updateTimeframe") private var updateTimeframe: Int = 1 +// @AppStorage("settings.updater.updateTimeframe") private var updateTimeframe: Int = 1 + @AppStorage("settings.updater.enableUpdates") private var enableUpdates: Bool = true @AppStorage("settings.permissions.hasLaunched") private var hasLaunched: Bool = false @AppStorage("displayMode") var displayMode: DisplayMode = .system @AppStorage("settings.general.mini") private var mini: Bool = false @@ -110,31 +111,29 @@ struct PearcleanerApp: App { // Make sure App Support folder exists in the future if needed for storage - //MARK: This is not needed any longer as the update file is stored in /tmp directory + //MARK: This is not needed any longer as the update file is stored in /tmp directory. Use in the future for any local db functions the app might need // ensureApplicationSupportFolderExists(appState: appState) // Check for updates after app launch checkAllPermissions(appState: appState) { results in appState.permissionResults = results if results.allPermissionsGranted { - loadGithubReleases(appState: appState) + // Get GH releases + loadGithubReleases(appState: appState, releaseOnly: true) + + if enableUpdates { + // Update checker + checkAndUpdateIfNeeded(appState: appState) + } + // Get new features getFeatures(appState: appState, features: $features) + // Load extra conditions from GitHub + loadConditionsFromGitHub() } } - // Load extra conditions from GitHub - loadConditionsFromGitHub() - // TIMERS //////////////////////////////////////////////////////////////////////////////////// - - // Check for app updates every 8 hours or whatever user saved setting. - let updateSeconds = updateTimeframe.daysToSeconds - _ = Timer.scheduledTimer(withTimeInterval: updateSeconds, repeats: true) { _ in - DispatchQueue.main.async { - loadGithubReleases(appState: appState) - } - } } #endif diff --git a/Pearcleaner/Settings/Update.swift b/Pearcleaner/Settings/Update.swift index 1c46a98..739c6e0 100644 --- a/Pearcleaner/Settings/Update.swift +++ b/Pearcleaner/Settings/Update.swift @@ -13,20 +13,62 @@ struct UpdateSettingsTab: View { @EnvironmentObject var appState: AppState @State private var showAlert = false @State private var showDone = false + @AppStorage("settings.updater.nextUpdateDate") private var nextUpdateDate = Date.now.timeIntervalSinceReferenceDate @AppStorage("settings.updater.updateTimeframe") private var updateTimeframe: Int = 1 + @AppStorage("settings.updater.enableUpdates") private var enableUpdates: Bool = true var body: some View { VStack { - - HStack { - Text("Check for updates every ") + - Text("**\(updateTimeframe)**").foregroundColor(.red) + - Text(updateTimeframe == 1 ? " day" : " days") - Stepper(value: $updateTimeframe, in: 1...7) { - Text("") + + HStack(spacing: 0) { + Image(systemName: "arrow.down.square") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .padding(.trailing) + .foregroundStyle(Color("mode").opacity(0.5)) + + VStack { + HStack(spacing: 0) { + Text("\(enableUpdates ? "Pearcleaner will check for updates every " : "Automatic updates are disabled")") + .font(.callout) + .foregroundStyle(Color("mode").opacity(0.5)) + + if enableUpdates { + Text("**\(updateTimeframe)**").font(.system(.callout, design: .monospaced)).monospacedDigit() + Text(updateTimeframe == 1 ? " day" : " days") + .font(.callout) + .foregroundStyle(Color("mode").opacity(0.5)) + + Stepper("", value: $updateTimeframe, in: 0...30) + .onChange(of: updateTimeframe, perform: { _ in + updateNextUpdateDate() + }) + } + Spacer() + } + + if enableUpdates { + HStack { + Text("Next update check: \(formattedDate(Date(timeIntervalSinceReferenceDate: nextUpdateDate)))") + .font(.footnote) + .foregroundStyle(Color("mode").opacity(0.3)) + Spacer() + } + } + + + } + + Spacer() + Toggle(isOn: $enableUpdates, label: { + }) + .toggleStyle(.switch) } - + .padding(5) + .padding(.leading) + ScrollView { VStack() { ForEach(appState.releases, id: \.id) { release in @@ -87,6 +129,10 @@ struct UpdateSettingsTab: View { } .padding(20) .frame(width: 500, height: 520) +// .onAppear { +// // Convert TimeInterval to Date on appearance +// let _ = Date(timeIntervalSinceReferenceDate: nextUpdateDate) +// } } }