diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj index 638e4d6de0..c1fe168859 100644 --- a/Nextcloud.xcodeproj/project.pbxproj +++ b/Nextcloud.xcodeproj/project.pbxproj @@ -5759,7 +5759,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -5825,7 +5825,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -6002,7 +6002,7 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/marinofaggiana/LucidBanner"; requirement = { - branch = 0.5.0; + branch = main; kind = branch; }; }; diff --git a/iOSClient/Favorites/NCFavorite.swift b/iOSClient/Favorites/NCFavorite.swift index cb5bd4cfb6..4a020f53af 100644 --- a/iOSClient/Favorites/NCFavorite.swift +++ b/iOSClient/Favorites/NCFavorite.swift @@ -40,8 +40,8 @@ class NCFavorite: NCCollectionViewCommon { override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) - stopSyncMetadata() Task { + await stopSyncMetadata() await NCNetworking.shared.networkingTasks.cancel(identifier: "NCFavorite") } } diff --git a/iOSClient/Files/NCFiles.swift b/iOSClient/Files/NCFiles.swift index 1285c12e85..c712f876b6 100644 --- a/iOSClient/Files/NCFiles.swift +++ b/iOSClient/Files/NCFiles.swift @@ -43,7 +43,10 @@ class NCFiles: NCCollectionViewCommon { } NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: nil) { _ in - self.stopSyncMetadata() + Task { + await self.stopSyncMetadata() + await self.searchOperationHandle.cancel() + } } if self.serverUrl.isEmpty { @@ -127,8 +130,8 @@ class NCFiles: NCCollectionViewCommon { override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) - stopSyncMetadata() Task { + await stopSyncMetadata() await NCNetworking.shared.networkingTasks.cancel(identifier: "\(self.serverUrl)_NCFiles") } } diff --git a/iOSClient/GUI/Lucid Banner/BannerView.swift b/iOSClient/GUI/Lucid Banner/BannerView.swift index 005a702a8b..a6815a88ed 100644 --- a/iOSClient/GUI/Lucid Banner/BannerView.swift +++ b/iOSClient/GUI/Lucid Banner/BannerView.swift @@ -4,6 +4,7 @@ import SwiftUI import LucidBanner +import Alamofire // MARK: - Show Banner #if !EXTENSION @@ -133,7 +134,7 @@ func showInfoBanner(scene: UIWindowScene?, backgroundColor: UIColor = .systemBackground, errorCode: Int? = nil) async { #if !EXTENSION - guard !bannerContainsErrorCode(errorCode: errorCode) else { + guard !bannerContainsError(errorCode: errorCode) else { return } let scene = scene ?? UIApplication.shared.mainAppWindow?.windowScene @@ -176,7 +177,8 @@ func showErrorBannerActiveScenes(title: String = "_error_", foregroundColor: UIColor = .white, backgroundColor: UIColor = .red, sleepBefore: Double = 1, - errorCode: Int) async { + errorCode: Int, + afError: AFError? = nil) async { for scene in UIApplication.shared.foregroundActiveScenes { await showErrorBanner(scene: scene, title: title, @@ -185,7 +187,8 @@ func showErrorBannerActiveScenes(title: String = "_error_", foregroundColor: foregroundColor, backgroundColor: backgroundColor, sleepBefore: sleepBefore, - errorCode: errorCode) + errorCode: errorCode, + afError: afError) } } @@ -197,7 +200,8 @@ func showErrorBanner(controller: UITabBarController?, foregroundColor: UIColor = .white, backgroundColor: UIColor = .red, sleepBefore: Double = 1, - errorCode: Int) async { + errorCode: Int, + afError: AFError? = nil) async { let scene = SceneManager.shared.getWindow(controller: controller)?.windowScene await showErrorBanner(scene: scene, text: text, @@ -205,7 +209,8 @@ func showErrorBanner(controller: UITabBarController?, foregroundColor: foregroundColor, backgroundColor: backgroundColor, sleepBefore: sleepBefore, - errorCode: errorCode) + errorCode: errorCode, + afError: afError) } @MainActor @@ -216,7 +221,8 @@ func showErrorBanner(sceneIdentifier: String?, foregroundColor: UIColor = .white, backgroundColor: UIColor = .red, sleepBefore: Double = 1, - errorCode: Int) async { + errorCode: Int, + afError: AFError? = nil) async { await showErrorBanner(controller: SceneManager.shared.getController(sceneIdentifier: sceneIdentifier), title: title, text: text, @@ -224,7 +230,8 @@ func showErrorBanner(sceneIdentifier: String?, foregroundColor: foregroundColor, backgroundColor: backgroundColor, sleepBefore: sleepBefore, - errorCode: errorCode) + errorCode: errorCode, + afError: afError) } #endif @@ -237,9 +244,10 @@ func showErrorBanner(scene: UIWindowScene?, foregroundColor: UIColor = .white, backgroundColor: UIColor = .red, sleepBefore: Double = 1, - errorCode: Int) async { + errorCode: Int, + afError: AFError? = nil) async { #if !EXTENSION - guard !bannerContainsErrorCode(errorCode: errorCode) else { + guard !bannerContainsError(errorCode: errorCode, afError: afError) else { return } let scene = scene ?? UIApplication.shared.mainAppWindow?.windowScene @@ -280,7 +288,7 @@ func showErrorBanner(scene: UIWindowScene?, // MARK: - Helper #if !EXTENSION -func bannerContainsErrorCode(errorCode: Int?) -> Bool { +func bannerContainsError(errorCode: Int?, afError: AFError? = nil) -> Bool { guard let errorCode else { return false } @@ -288,6 +296,9 @@ func bannerContainsErrorCode(errorCode: Int?) -> Bool { if errorCode == -999 { return true } + if let afError, case .explicitlyCancelled = afError { + return true + } // Prevent repeated display of the same user-facing error during the current foreground session. // If this error code has already been shown, do nothing. // Otherwise, record it and allow the UX notification to be displayed once. @@ -382,3 +393,4 @@ struct MessageBannerView: View { .padding() } } + diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon+Search.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon+Search.swift index 066bcb83f7..24ce38a348 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon+Search.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon+Search.swift @@ -4,6 +4,7 @@ import Foundation import NextcloudKit +import Alamofire extension NCCollectionViewCommon { @MainActor @@ -18,7 +19,7 @@ extension NCCollectionViewCommon { self.networkSearchInProgress = true // STOP PREEMPTIVE SYNC METADATA - self.stopSyncMetadata() + await self.stopSyncMetadata() // Clear datasotce self.dataSource.removeAll() self.collectionView.reloadData() @@ -112,24 +113,17 @@ extension NCCollectionViewCommon { self.collectionView.reloadData() // ---> Get providers - let results = await NextcloudKit.shared.unifiedSearchProviders(account: session.account) { _ in + let results = await NextcloudKit.shared.unifiedSearchProviders(account: session.account, handle: searchOperationHandle) { _ in // example filter // ["calendar", "files", "fulltextsearch"].contains(provider.id) return true - } taskHandler: { task in - Task { - let identifier = await NCNetworking.shared.networkingTasks.createIdentifier( - account: self.session.account, - name: "unifiedSearchProviders" - ) - await NCNetworking.shared.networkingTasks.track(identifier: identifier, task: task) - self.searchTask = task - self.collectionView.reloadData() - } } if results.error != .success { - await showErrorBanner(controller: self.controller, text: results.error.errorDescription, errorCode: results.error.errorCode) + await showErrorBanner(controller: self.controller, + text: results.error.errorDescription, + errorCode: results.error.errorCode, + afError: results.error.error as? AFError) } guard isSearchingMode, @@ -154,21 +148,15 @@ extension NCCollectionViewCommon { limit: 5, cursor: 0, timeout: 90, - account: session.account - ) { task in - Task { - let identifier = await NCNetworking.shared.networkingTasks.createIdentifier( - account: self.session.account, - name: "unifiedSearch" - ) - await NCNetworking.shared.networkingTasks.track(identifier: identifier, task: task) - self.searchTask = task - self.collectionView.reloadData() - } - } + account: session.account, + handle: searchOperationHandle + ) if results.error != .success { - await showErrorBanner(controller: self.controller, text: results.error.errorDescription, errorCode: results.error.errorCode) + await showErrorBanner(controller: self.controller, + text: results.error.errorDescription, + errorCode: results.error.errorCode, + afError: results.error.error as? AFError) } guard isSearchingMode, @@ -217,21 +205,15 @@ extension NCCollectionViewCommon { limit: 5, cursor: cursor, timeout: 60, - account: session.account - ) { task in - Task { - let identifier = await NCNetworking.shared.networkingTasks.createIdentifier( - account: self.session.account, - name: "unifiedSearch" - ) - await NCNetworking.shared.networkingTasks.track(identifier: identifier, task: task) - self.searchTask = task - self.collectionView.reloadData() - } - } + account: session.account, + handle: searchOperationHandle + ) if results.error != .success { - await showErrorBanner(controller: self.controller, text: results.error.errorDescription, errorCode: results.error.errorCode) + await showErrorBanner(controller: self.controller, + text: results.error.errorDescription, + errorCode: results.error.errorCode, + afError: results.error.error as? AFError) } guard isSearchingMode, @@ -339,3 +321,4 @@ extension NCCollectionViewCommon { return metadata } } + diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon+SyncMetadata.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon+SyncMetadata.swift index 365f38d923..5e0254858d 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon+SyncMetadata.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon+SyncMetadata.swift @@ -42,7 +42,7 @@ extension NCCollectionViewCommon { /// Cancels the running sync task (if any) and releases the reference. /// /// Use this when the page/screen is about to disappear or the user navigates away. - func stopSyncMetadata() { + func stopSyncMetadata() async { if let task = syncMetadatasTask { if task.isCancelled { nkLog(tag: global.logTagSpeedUpSyncMetadata, emoji: .stop, message: "Sync Metadata for \(self.serverUrl) was already cancelled.", consoleOnly: true) diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift index e639a046f3..8a20c4d6d6 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift @@ -49,6 +49,7 @@ class NCCollectionViewCommon: UIViewController, NCAccountSettingsModelDelegate, var syncMetadatasTask: Task? // Search var isSearchingMode: Bool = false + var searchOperationHandle = NKOperationHandle() var searchTask: URLSessionTask? var searchResultText: String? var searchResultStore: String? @@ -265,6 +266,24 @@ class NCCollectionViewCommon: UIViewController, NCAccountSettingsModelDelegate, DispatchQueue.main.async { self.collectionView?.collectionViewLayout.invalidateLayout() } + + Task { + for await event in await searchOperationHandle.events() { + switch event { + case .didSetTask(let task): + searchTask = task + if dataSource.isEmpty() { + collectionView.reloadData() + } + case .didSetRequest(let request): + print("Request available:", request) + case .didCancel: + print("Operation cancelled") + case .didClear: + print("Handle cleared") + } + } + } } override func viewWillAppear(_ animated: Bool) { @@ -315,13 +334,13 @@ class NCCollectionViewCommon: UIViewController, NCAccountSettingsModelDelegate, override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) - - self.searchTask?.cancel() dismissTip() // Cancel Queue & Retrieves Properties self.networking.downloadThumbnailQueue.cancelAll() - searchTask?.cancel() + Task { + await searchOperationHandle.cancel() + } } override func viewDidDisappear(_ animated: Bool) { @@ -516,13 +535,13 @@ class NCCollectionViewCommon: UIViewController, NCAccountSettingsModelDelegate, // mainNavigationController?.hiddenPlusButton(false) - self.searchTask?.cancel() self.isSearchingMode = false self.networkSearchInProgress = false self.searchResultText = nil self.searchResultStore = nil Task { + await searchOperationHandle.cancel() self.dataSource.removeAll() await self.reloadDataSource() } diff --git a/iOSClient/Shares/NCShares.swift b/iOSClient/Shares/NCShares.swift index c00af453eb..79674d943f 100644 --- a/iOSClient/Shares/NCShares.swift +++ b/iOSClient/Shares/NCShares.swift @@ -43,8 +43,8 @@ class NCShares: NCCollectionViewCommon { override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) - stopSyncMetadata() Task { + await stopSyncMetadata() await NCNetworking.shared.networkingTasks.cancel(identifier: "NCShares") backgroundTask?.cancel() }