FileManager.trashItem(at:resultingItemURL:) never returns if called inside NSFileCoordinator.coordinate(writingItemAt:options:error:byAccessor:)

In the following code, the string "after" is never printed:


class ViewController: UIViewController, UIDocumentPickerDelegate {

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        let documentPicker = UIDocumentPickerViewController(documentTypes: [kUTTypeText as String], in: .open)
        documentPicker.delegate = self
        present(documentPicker, animated: true)
    }

    func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
        let url = urls[0]
        guard url.startAccessingSecurityScopedResource() else {
            return
        }
        defer {
            url.stopAccessingSecurityScopedResource()
        }
        DispatchQueue.global(qos: .background).async {
            var fcError, _error: NSError?
            NSFileCoordinator(filePresenter: nil).coordinate(writingItemAt: url, options: [.forMoving], error: &fcError, byAccessor: { url in
                print("before")
                do {
                    try FileManager.default.trashItem(at: url, resultingItemURL: nil)
//                    try FileManager.default.moveItem(at: url, to: url.deletingLastPathComponent().appendingPathComponent("bla"))
                } catch {
                    _error = error as NSError
                }
                print("after")
            })
            print(fcError ?? _error)
        }
    }

}

Other file operations (such as moving a file, see commented out line) work as expected. Commenting out the part with the NSFileCoordinator seems to solve the problem.

Replies

Shouldn't you use ".forDeleting" instead?

I don't think so, because the file is moved to the trash and not permanently deleted. Anyway, i tried it and it's still the same.

Did you ever solve this?

I'm running into the same problem: FileManager.trashItem() deadlocks when wrapped in NSFileCoordinator.coordinate(writingItemAt:) on macOS Catalina. The code works fine on macOS Mojave.


From the stacktrace, it looks like FileManager uses its own NSFileCoordination block and that can't get access to the file, because of the outer block:

Did you ever solve this? I'm running into the same problem: FileManager.trashItem() deadlocks when wrapped in NSFileCoordinator.coordinate(writingItemAt:) on macOS Catalina. The code works fine on macOS Mojave. From the stacktrace, it looks like FileManager uses its own NSFileCoordination block and that can't get access to the file, because of the outer block:
  • 2692 static MoveToTrashOperation._trashItem(_:parentIsAttachmentsFolder:fileNamesOfFilesThatCanBeDeleted:changePropagator:) (in AppFramework) + 474 [0x10f497d0a]

  • 2692 NSFileManager.coordinatedTrashItem(at:) (in AppFramework) + 711 [0x10fc12727]

// This is my file access block:
  • 2692 NSFileCoordinator.coordinate<A>(writingItemAt:options:byAccessor:) (in AppFramework) + 858 [0x10fc0deca]

  • 2692 -[NSFileCoordinator coordinateWritingItemAtURL:options:error:byAccessor:] (in Foundation) + 91 [0x7fff35294473]

  • 2692 -[NSFileCoordinator(NSPrivate) _coordinateWritingItemAtURL:options:error:byAccessor:] (in Foundation) + 742 [0x7fff35294774]

  • 2692 -[NSFileCoordinator(NSPrivate) _withAccessArbiter:invokeAccessor:orDont:andRelinquishAccessClaim:] (in Foundation) + 531 [0x7fff3518b68f]

  • 2692 __85-[NSFileCoordinator(NSPrivate) _coordinateWritingItemAtURL:options:error:byAccessor:]_block_invoke.340 (in Foundation) + 219 [0x7fff353d165a]

// A second time?
  • 2692 __73-[NSFileCoordinator coordinateWritingItemAtURL:options:error:byAccessor:]_block_invoke (in Foundation) + 102 [0x7fff35294905]

  • 2692 -[NSFileCoordinator _invokeAccessor:thenCompletionHandler:] (in Foundation) + 143 [0x7fff35247b74]

  • 2692 thunk for @escaping @callee_guaranteed (@in_guaranteed URL) -> () (in AppFramework) + 141 [0x10fc0bf8d]

  • 2692 thunk for @callee_guaranteed (@in_guaranteed URL) -> () (in AppFramework) + 12 [0x10fc0beac]

  • 2692 closure #1 in NSFileCoordinator.coordinate<A>(writingItemAt:options:byAccessor:) (in AppFramework) + 225 [0x10fc0e281]

  • 2692 partial apply for closure #1 in NSFileManager.coordinatedTrashItem(at:) (in AppFramework) + 16 [0x10fc12b60]

  • 2692 closure #1 in NSFileManager.coordinatedTrashItem(at:) (in AppFramework) + 363 [0x10fc1293b]

  • 2692 -[NSFileManager trashItemAtURL:resultingItemURL:error:] (in Foundation) + 156 [0x7fff352d3f0c]

  • 2692 -[NSFileManager(FPAdditions) fp_trashItemAtURL:resultingItemURL:error:] (in FileProvider) + 454 [0x7fff3506ff1a]

  • 2692 _dispatch_semaphore_wait_slow (in libdispatch.dylib) + 98 [0x7fff6ca04fbf]

  • 2692 _dispatch_sema4_wait (in libdispatch.dylib) + 16 [0x7fff6ca04aed]

  • 2692 semaphore_wait_trap (in libsystem_kernel.dylib) + 10 [0x7fff6cb9ee36]


Unfortunately I don't have a solution. My workaround was to comment out the file coordinator line.