11// SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
22// SPDX-License-Identifier: LGPL-3.0-or-later
33
4- import FileProvider
4+ @ preconcurrency import FileProvider
55import NextcloudKit
66
77///
88/// The `NSFileProviderEnumerator` implementation to enumerate file provider items and related change sets.
99///
10- public class Enumerator : NSObject , NSFileProviderEnumerator {
10+ final public class Enumerator : NSObject , NSFileProviderEnumerator , Sendable {
1111 let enumeratedItemIdentifier : NSFileProviderItemIdentifier
12- private var enumeratedItemMetadata : SendableItemMetadata ?
12+ private let enumeratedItemMetadata : SendableItemMetadata ?
13+
14+ private var enumeratingSystemIdentifier : Bool {
15+ Self . isSystemIdentifier ( enumeratedItemIdentifier)
16+ }
1317
1418 let domain : NSFileProviderDomain ?
1519 let dbManager : FilesDatabaseManager
@@ -19,8 +23,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator {
1923 let logger : FileProviderLogger
2024 let account : Account
2125 let remoteInterface : RemoteInterface
22- var isInvalidated = false
23- private( set) var serverUrl : String = " "
26+ let serverUrl : String
2427
2528 private static func isSystemIdentifier( _ identifier: NSFileProviderItemIdentifier ) -> Bool {
2629 identifier == . rootContainer || identifier == . trashContainer || identifier == . workingSet
@@ -46,6 +49,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator {
4649 if Self . isSystemIdentifier ( enumeratedItemIdentifier) {
4750 logger. info ( " Providing enumerator for a system defined container. " , [ . item: enumeratedItemIdentifier] )
4851 serverUrl = account. davFilesUrl
52+ enumeratedItemMetadata = nil
4953 } else {
5054 logger. debug ( " Providing enumerator for item with identifier. " , [ . item: enumeratedItemIdentifier] )
5155 enumeratedItemMetadata = dbManager. itemMetadata (
@@ -54,6 +58,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator {
5458 if let enumeratedItemMetadata {
5559 serverUrl = enumeratedItemMetadata. serverUrl + " / " + enumeratedItemMetadata. fileName
5660 } else {
61+ serverUrl = " "
5762 logger. error ( " Could not find itemMetadata for file with identifier. " , [ . item: enumeratedItemIdentifier] )
5863 }
5964 }
@@ -64,7 +69,6 @@ public class Enumerator: NSObject, NSFileProviderEnumerator {
6469
6570 public func invalidate( ) {
6671 logger. debug ( " Enumerator is being invalidated. " , [ . item: enumeratedItemIdentifier] )
67- isInvalidated = true
6872 }
6973
7074 // MARK: - Protocol methods
@@ -88,8 +92,12 @@ public class Enumerator: NSObject, NSFileProviderEnumerator {
8892 if enumeratedItemIdentifier == . trashContainer {
8993 logger. info ( " Enumerating trash. " , [ . account: account. ncKitAccount, . url: serverUrl] )
9094
91- Task {
92- let ( _, capabilities, _, error) = await remoteInterface. currentCapabilities ( account: account, options: . init( ) , taskHandler: { _ in } )
95+ Task { [ weak self] in
96+ guard let self else {
97+ return
98+ }
99+
100+ let ( _, capabilities, _, error) = await remoteInterface. currentCapabilities ( account: account)
93101
94102 guard let capabilities, error == . success else {
95103 logger. error ( " Could not acquire capabilities, cannot check trash. " , [ . error: error] )
@@ -103,24 +111,27 @@ public class Enumerator: NSObject, NSFileProviderEnumerator {
103111 return
104112 }
105113
106- let ( _, trashedItems, _, trashReadError) = await remoteInterface. trashedItems (
107- account: account,
114+ let domain = self . domain
115+ let enumeratedItemIdentifier = self . enumeratedItemIdentifier
116+
117+ let ( _, trashedItems, _, trashReadError) = await remoteInterface. listingTrashAsync (
118+ filename: nil ,
119+ showHiddenFiles: true ,
120+ account: account. ncKitAccount,
108121 options: . init( ) ,
109122 taskHandler: { task in
110- if let domain = self . domain {
123+ if let domain {
111124 NSFileProviderManager ( for: domain) ? . register (
112125 task,
113- forItemWithIdentifier: self . enumeratedItemIdentifier,
126+ forItemWithIdentifier: enumeratedItemIdentifier,
114127 completionHandler: { _ in }
115128 )
116129 }
117130 }
118131 )
119132
120133 guard trashReadError == . success else {
121- let error = trashReadError. fileProviderError (
122- handlingNoSuchItemErrorUsingItemIdentifier: self . enumeratedItemIdentifier
123- ) ?? NSFileProviderError ( . cannotSynchronize)
134+ let error = trashReadError. fileProviderError ( handlingNoSuchItemErrorUsingItemIdentifier: enumeratedItemIdentifier) ?? NSFileProviderError ( . cannotSynchronize)
124135 observer. finishEnumeratingWithError ( error)
125136 return
126137 }
@@ -131,10 +142,11 @@ public class Enumerator: NSObject, NSFileProviderEnumerator {
131142 remoteInterface: remoteInterface,
132143 dbManager: dbManager,
133144 numPage: 1 ,
134- trashItems: trashedItems,
145+ trashItems: trashedItems ?? [ ] ,
135146 log: logger. log
136147 )
137148 }
149+
138150 return
139151 }
140152
@@ -271,15 +283,19 @@ public class Enumerator: NSObject, NSFileProviderEnumerator {
271283 } else if enumeratedItemIdentifier == . trashContainer {
272284 logger. debug ( " Enumerating changes in trash. " , [ . account: account. ncKitAccount] )
273285
274- Task {
275- let ( _, capabilities, _, error) = await remoteInterface. currentCapabilities (
276- account: account, options: . init( ) , taskHandler: { _ in }
277- )
286+ Task { [ weak self] in
287+ guard let self else {
288+ return
289+ }
290+
291+ let ( _, capabilities, _, error) = await remoteInterface. currentCapabilities ( account: account)
292+
278293 guard let capabilities, error == . success else {
279294 logger. error ( " Could not acquire capabilities, cannot check trash. " , [ . error: error] )
280295 observer. finishEnumeratingWithError ( NSFileProviderError ( . serverUnreachable) )
281296 return
282297 }
298+
283299 guard capabilities. files? . undelete == true else {
284300 logger. error ( " Trash is unsupported on server. Cannot enumerate changes. " )
285301
@@ -289,24 +305,27 @@ public class Enumerator: NSObject, NSFileProviderEnumerator {
289305 return
290306 }
291307
292- let ( _, trashedItems, _, trashReadError) = await remoteInterface. trashedItems (
293- account: account,
308+ let domain = self . domain
309+ let enumeratedItemIdentifier = self . enumeratedItemIdentifier
310+
311+ let ( _, trashedItems, _, trashReadError) = await remoteInterface. listingTrashAsync (
312+ filename: nil ,
313+ showHiddenFiles: true ,
314+ account: account. ncKitAccount,
294315 options: . init( ) ,
295316 taskHandler: { task in
296- if let domain = self . domain {
317+ if let domain {
297318 NSFileProviderManager ( for: domain) ? . register (
298319 task,
299- forItemWithIdentifier: self . enumeratedItemIdentifier,
320+ forItemWithIdentifier: enumeratedItemIdentifier,
300321 completionHandler: { _ in }
301322 )
302323 }
303324 }
304325 )
305326
306327 guard trashReadError == . success else {
307- let error = trashReadError. fileProviderError (
308- handlingNoSuchItemErrorUsingItemIdentifier: self . enumeratedItemIdentifier
309- ) ?? NSFileProviderError ( . cannotSynchronize)
328+ let error = trashReadError. fileProviderError ( handlingNoSuchItemErrorUsingItemIdentifier: self . enumeratedItemIdentifier) ?? NSFileProviderError ( . cannotSynchronize)
310329 observer. finishEnumeratingWithError ( error)
311330 return
312331 }
@@ -317,7 +336,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator {
317336 account: account,
318337 remoteInterface: remoteInterface,
319338 dbManager: dbManager,
320- trashItems: trashedItems,
339+ trashItems: trashedItems ?? [ ] ,
321340 log: logger. log
322341 )
323342 }
@@ -330,7 +349,11 @@ public class Enumerator: NSObject, NSFileProviderEnumerator {
330349 // No matter what happens here we finish enumeration in some way, either from the error
331350 // handling below or from the completeChangesObserver
332351 // TODO: Move to the sync engine extension
333- Task {
352+ Task { [ weak self] in
353+ guard let self else {
354+ return
355+ }
356+
334357 let (
335358 _, newMetadatas, updatedMetadatas, deletedMetadatas, _, readError
336359 ) = await Self . readServerUrl (
0 commit comments