Is there a way to know a file is not found in iCloud Document by using NSMetadataQuery and NotificationCenter?

I'm using NSMetadataQuery and NotificationCenter, to perform file downloading from iCloud.

  1. Construct NSMetadataQuery with predicate NSPredicate(format: "%K == %@", NSMetadataItemFSNameKey, filename)
  2. Observe NSMetadataQueryDidUpdate & NSMetadataQueryDidFinishGathering using NotificationCenter.
  3. Check file status NSMetadataUbiquitousItemDownloadingStatusKey. If the file is up-to-date, copy the file to destination directory, and jump to step 6.
  4. Perform FileManager.default.startDownloadingUbiquitousItem
  5. Receive file downloading status in NSMetadataQueryDidUpdate callback. If the file is up-to-date, copy the file to destination directory, and jump to step 6.
  6. Perform cleanup by removing all observers.

If file is not available in iCloud, no notification received.


We wish, even if the file doesn't exist, we will still be notified, so that we have chance to perform cleanup (step 6)

Here's the code snippet to perform iCloud download.

DownloadManager.swift

class DownloadManager {
    static let INSTANCE = DownloadManager()

    var downloaders = [iCloudDocumentDownloader]()

    private init() {
    }
    
    func append(filename: String, destinationDirectory: URL) {
        let downloader = iCloudDocumentDownloader(filename: filename, destinationDirectory: destinationDirectory)
        downloaders.append(downloader)
    }
    
    func removeAll(_ downloader: iCloudDocumentDownloader) {
        downloaders.removeAll{$0 === downloader}
    }
}

iCloudDocumentDownloader.swift

class iCloudDocumentDownloader {
    private let filename: String
    private let destinationDirectory: URL
    
    private let metadataQuery = NSMetadataQuery()
    
    private static let operationQueue: OperationQueue = {
        let operationQueue = OperationQueue()
        operationQueue.name = "com.yocto.wenote.operationQueueForiCloudDocument"
        operationQueue.maxConcurrentOperationCount = 1
        operationQueue.qualityOfService = .userInitiated
        return operationQueue
    }()
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
    private func bye() {
        DownloadManager.INSTANCE.removeAll(self)
    }
    
    init(filename: String, destinationDirectory: URL) {
        self.filename = filename
        self.destinationDirectory = destinationDirectory
        
        metadataQuery.operationQueue = iCloudDocumentDownloader.operationQueue
        
        metadataQuery.predicate = NSPredicate(format: "%K == %@", NSMetadataItemFSNameKey, filename)
        
        metadataQuery.searchScopes = [
            NSMetadataQueryUbiquitousDocumentsScope
        ]
        
        NotificationCenter.default.addObserver(self, selector: #selector(didUpdate), name: NSNotification.Name.NSMetadataQueryDidUpdate, object: metadataQuery)
        NotificationCenter.default.addObserver(self, selector: #selector(didFinishGathering), name: NSNotification.Name.NSMetadataQueryDidFinishGathering, object: metadataQuery)
        
        metadataQuery.start()
    }
    
    @objc func didUpdate(_ notification: Notification) {
        guard let metadataQuery = notification.object as? NSMetadataQuery else { return }
        
        metadataQuery.enumerateResults { [weak self] (item: Any, index: Int, stop: UnsafeMutablePointer<ObjCBool>) in
            guard let self = self else { return }
            
            guard let metadataItem = item as? NSMetadataItem else { return }
            guard let status = metadataItem.value(forAttribute: NSMetadataUbiquitousItemDownloadingStatusKey) as? String else { return }
            guard let url = metadataItem.value(forAttribute: NSMetadataItemURLKey) as? URL else { return }
            
            if status == NSMetadataUbiquitousItemDownloadingStatusCurrent {
                if !destinationDirectory.createCompleteDirectoryHierarchyIfDoesNotExist() {
                    self.bye()
                    // Early return.
                    return
                }
                
                let destinationURL = destinationDirectory.appendingPathComponent(filename, isDirectory: false)
                
                do {
                    try FileManager.default.copyItem(at: url, to: destinationURL)
                } catch {
                    error_log(error)
                }
                self.bye()
            } else if let error = metadataItem.value(forAttribute: NSMetadataUbiquitousItemDownloadingErrorKey) as? NSError {
                error_log(error)
                self.bye()
            } else {
            }
        }
    }
    
    @objc func didFinishGathering(_ notification: Notification) {
        guard let metadataQuery = notification.object as? NSMetadataQuery else { return }
        
        metadataQuery.enumerateResults { [weak self] (item: Any, index: Int, stop: UnsafeMutablePointer<ObjCBool>) in
            guard let self = self else { return }
            
            guard let metadataItem = item as? NSMetadataItem else { return }
            guard let status = metadataItem.value(forAttribute: NSMetadataUbiquitousItemDownloadingStatusKey) as? String else { return }
            guard let url = metadataItem.value(forAttribute: NSMetadataItemURLKey) as? URL else { return }
            
            if status == NSMetadataUbiquitousItemDownloadingStatusCurrent {
                if !destinationDirectory.createCompleteDirectoryHierarchyIfDoesNotExist() {
                    self.bye()
                    // Early return.
                    return
                }
                
                let destinationURL = destinationDirectory.appendingPathComponent(filename, isDirectory: false)
                
                do {
                    try FileManager.default.copyItem(at: url, to: destinationURL)
                } catch {
                    error_log(error)
                }
                self.bye()
            } else if let error = metadataItem.value(forAttribute: NSMetadataUbiquitousItemDownloadingErrorKey) as? NSError {
                error_log(error)
                self.bye()
            } else {
                do {
                    try FileManager.default.startDownloadingUbiquitousItem(at: url)
                } catch {
                    error_log(error)
                    self.bye()
                }
            }
        }
    }
}
Is there a way to know a file is not found in iCloud Document by using NSMetadataQuery and NotificationCenter?
 
 
Q