Search code examples
swiftmacosprogresskey-value-observingobservation

How to observe progress of fileCompletedCount with Swift keypath?


I am trying to replace old fashioned Objective-C way of observation to modern swift way of observation. However I cannot figure out the keypath.

//doesn't work
progress.observe(\.userInfo[ProgressUserInfoKey.fileCompletedCountKey], changeHandler: {_,_ in
        
})

Foundation/NSObject.swift:132: Fatal error: Could not extract a String from KeyPath \NSProgress.userInfo.<computed 0x00000001a5a1ecf0 (Optional<Any>)>

PS: You cannot observe fileCompletedCount because is a computed property.

PS2: I don't think this is a duplicate question. See the image: it is being made manually a dynamic property.

Old code:

import Cocoa
private var viewControllerObservationContext = 0

class ViewController: NSViewController {

    private var progress: Progress = {
        let progress = Progress()
        progress.kind = .file
        progress.fileTotalCount = 20
        return progress
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let filesCompletedKeyPath = "userInfo.NSProgressFileCompletedCountKey"
        progress.addObserver(self, forKeyPath: filesCompletedKeyPath, options: .new, context: &viewControllerObservationContext)
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            self.progress.fileCompletedCount = 10
        }
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
        guard context == &viewControllerObservationContext else {
            super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
            return
        }
        print(object)
        print(keyPath)
    }

    deinit {
        progress.removeObserver(self, forKeyPath: "userInfo.NSProgressFileCompletedCountKey")
    }
}

enter image description here


Solution

  • Observe the localizedDescription to get all updates including fileCompletedCount and fileTotalCount

    let progress = Progress()
    progress.observe(\.localizedDescription) { p, c in
        print(p.fileTotalCount)
        print(p.fileCompletedCount)
    }
    progress.fileCompletedCount = 10
    progress.fileTotalCount = 11
    
    nil
    Optional(10)
    Optional(11)
    Optional(10)