diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj index 05654d1b07..18774c15d2 100644 --- a/Nextcloud.xcodeproj/project.pbxproj +++ b/Nextcloud.xcodeproj/project.pbxproj @@ -92,6 +92,9 @@ AFCE353527E4ED5900FEA6C2 /* DateFormatter+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */; }; AFCE353727E4ED7B00FEA6C2 /* NCShareCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */; }; AFCE353927E5DE0500FEA6C2 /* Shareable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* Shareable.swift */; }; + AFCE353927E5DE0500FEA6C2 /* NCShare+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */; }; + B5D45E732DA5172900773929 /* AppUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D45E712DA5172900773929 /* AppUpdater.swift */; }; + C04E2F232A17BB4D001BAD85 /* FilesIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C04E2F222A17BB4D001BAD85 /* FilesIntegrationTests.swift */; }; D575039F27146F93008DC9DC /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extension.swift */; }; D5B6AA7827200C7200D49C24 /* NCActivityTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */; }; F310B1EF2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = F310B1EE2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift */; }; @@ -1330,6 +1333,8 @@ AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+Extension.swift"; sourceTree = ""; }; AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCells.swift; sourceTree = ""; }; AFCE353827E5DE0400FEA6C2 /* Shareable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shareable.swift; sourceTree = ""; }; + AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCShare+Helper.swift"; sourceTree = ""; }; + B5D45E712DA5172900773929 /* AppUpdater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdater.swift; sourceTree = ""; }; C0046CDA2A17B98400D87C9D /* NextcloudUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; C04E2F202A17BB4D001BAD85 /* NextcloudIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCActivityTableViewCell.swift; sourceTree = ""; }; @@ -2156,6 +2161,14 @@ path = Advanced; sourceTree = ""; }; + B5D45E722DA5172900773929 /* AppUpdate */ = { + isa = PBXGroup; + children = ( + B5D45E712DA5172900773929 /* AppUpdater.swift */, + ); + path = AppUpdate; + sourceTree = ""; + }; C0046CDB2A17B98400D87C9D /* NextcloudUITests */ = { isa = PBXGroup; children = ( @@ -3301,6 +3314,7 @@ isa = PBXGroup; children = ( AA517BB42D66149900F8D37C /* .tx */, + B5D45E722DA5172900773929 /* AppUpdate */, F702F2CC25EE5B4F008F8E80 /* AppDelegate.swift */, F794E13E2BBC0F70003693D7 /* SceneDelegate.swift */, F77DD6A72C5CC093009448FB /* NCSession.swift */, @@ -4667,6 +4681,7 @@ F7D4BF402CA2E8D800A5E746 /* TOPasscodeButtonLabel.m in Sources */, F7D4BF412CA2E8D800A5E746 /* TOPasscodeViewControllerAnimatedTransitioning.m in Sources */, F7D4BF422CA2E8D800A5E746 /* TOPasscodeSettingsViewController.m in Sources */, + B5D45E732DA5172900773929 /* AppUpdater.swift in Sources */, F7D4BF432CA2E8D800A5E746 /* TOPasscodeCircleImage.m in Sources */, F7D4BF442CA2E8D800A5E746 /* TOPasscodeView.m in Sources */, F7D4BF452CA2E8D800A5E746 /* TOPasscodeCircleButton.m in Sources */, diff --git a/iOSClient/AppUpdate/AppUpdater.swift b/iOSClient/AppUpdate/AppUpdater.swift new file mode 100644 index 0000000000..9fab18b4ff --- /dev/null +++ b/iOSClient/AppUpdate/AppUpdater.swift @@ -0,0 +1,91 @@ +// +// AppUpdater.swift +// Nextcloud +// +// Created by Amrut Waghmare on 09/10/23. +// Copyright © 2023 Marino Faggiana. All rights reserved. +// + +import Foundation +import FirebaseRemoteConfig + +struct AppUpdaterKey { + static let lastUpdateCheckDate : String = "lastUpdateCheckDate" +} + +class AppUpdater { + func checkForUpdate() { + checkUpdate{ (version, isForceupdate) in + DispatchQueue.main.async { + if let version = version, let isForceupdate = isForceupdate { + if (isForceupdate) { + self.showUpdateAlert(version: version, isForceUpdate: isForceupdate) + } else { + if self.checkLastUpdate() { + self.showUpdateAlert(version: version, isForceUpdate: isForceupdate) + } + } + } + } + } + } + + func showUpdateAlert(version: String, isForceUpdate: Bool){ + guard let appDelegate = UIApplication.shared.delegate as? AppDelegate, let viewControlller = appDelegate.window?.rootViewController else { return } + let descriptionMsg = String(format: NSLocalizedString("update_description", comment: ""), version) + let alert = UIAlertController(title: NSLocalizedString("update_available", comment: ""), message: descriptionMsg, preferredStyle: .alert) + let updateAction = UIAlertAction(title: NSLocalizedString("update", comment: ""), style: .default, handler: { action in + guard let url = URL(string: NCBrandOptions.shared.appStoreUrl) else { return } + UIApplication.shared.open(url) + }) + alert.addAction(updateAction) + if !isForceUpdate { + alert.addAction(UIAlertAction(title: NSLocalizedString("not_now", comment: ""), style: .default, handler: { action in + self.saveAppUpdateCheckDate() + })) + } + alert.preferredAction = updateAction + viewControlller.present(alert, animated: true, completion: {}) + } + + func checkLastUpdate() -> Bool { + if let lastUpdateCheckDate = UserDefaults.standard.object(forKey: AppUpdaterKey.lastUpdateCheckDate) as? Date { + return daysBetweenDate(from: lastUpdateCheckDate) > 7 + } else { + return true + } + } + + func checkUpdate(completion: @escaping (String?, Bool?) -> Void) { + let remoteConfig = RemoteConfig.remoteConfig() + remoteConfig.fetch(withExpirationDuration: 1) { (status, error) in + if status == .success { + remoteConfig.activate { value, error in + // Remote config values fetched successfully + let iOSVersion = remoteConfig["ios_app_version"].stringValue ?? "default_value" + let isForcheUpdate = remoteConfig["ios_force_update"].boolValue + if let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String { + if iOSVersion != currentVersion { + // There is an update available + completion(iOSVersion,isForcheUpdate) + } else { + completion(nil, nil) + } + } + } + } else { + // Handle error + print("Error fetching remote config: \(error?.localizedDescription ?? "Unknown error")") + completion(nil, nil) + } + } + } + + func saveAppUpdateCheckDate() { + UserDefaults.standard.setValue(Date(), forKey: AppUpdaterKey.lastUpdateCheckDate) + } + + func daysBetweenDate(from date: Date) -> Int { + return Calendar.current.dateComponents([.day], from: date, to: Date()).day ?? 0 + } +} diff --git a/iOSClient/SceneDelegate.swift b/iOSClient/SceneDelegate.swift index d4d507c074..129b643713 100644 --- a/iOSClient/SceneDelegate.swift +++ b/iOSClient/SceneDelegate.swift @@ -77,7 +77,11 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } else { NCKeychain().removeAll() if let bundleID = Bundle.main.bundleIdentifier { + let lastUpdateCheckDate = UserDefaults.standard.object(forKey: AppUpdaterKey.lastUpdateCheckDate) UserDefaults.standard.removePersistentDomain(forName: bundleID) + if lastUpdateCheckDate != nil { + UserDefaults.standard.setValue(lastUpdateCheckDate, forKey: AppUpdaterKey.lastUpdateCheckDate) + } } if NCBrandOptions.shared.disable_intro { appDelegate.openLogin(selector: NCGlobal.shared.introLogin, window: window) @@ -118,7 +122,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { requestedAccount(controller: controller) } } - +// AppUpdater().checkForUpdate() NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterRichdocumentGrabFocus) } @@ -127,6 +131,20 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { let controller = SceneManager.shared.getController(scene: scene) NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Scene did become active") + let oldVersion = UserDefaults.standard.value(forKey: NCSettingsBundleHelper.SettingsBundleKeys.BuildVersionKey) as? String + AppUpdater().checkForUpdate() + AnalyticsHelper.shared.trackAppVersion(oldVersion: oldVersion) + if let userAccount = NCManageDatabase.shared.getActiveTableAccount() { + AnalyticsHelper.shared.trackUsedStorageData(quotaUsed: userAccount.quotaUsed) + } + + NCSettingsBundleHelper.setVersionAndBuildNumber() + NCSettingsBundleHelper.checkAndExecuteSettings(delay: 0.5) + +// if !NCAskAuthorization().isRequesting { +// NCPasscode.shared.hidePrivacyProtectionWindow() +// } + hidePrivacyProtectionWindow() NCAutoUpload.shared.initAutoUpload(controller: nil, account: session.account) { num in