5

I am having trouble with the new observe API in Swift 4.

player = AVPlayer()
player?.observe(\.currentItem.status, options: [.new], changeHandler: { [weak self] (player, newValue) in
    if let status = AVPlayer.Status(rawValue: (newValue as! NSNumber).intValue) {

   }
 }

But I get an error

Type of expression is ambiguous without more context.

How do I fix it? Not sure about keyPath syntax.

There is also a warning in extracting AVPlayerStatus in the closure above

Cast from 'NSKeyValueObservedChange' to unrelated type 'NSNumber' always fails"

matt
  • 515,959
  • 87
  • 875
  • 1,141
Deepak Sharma
  • 5,577
  • 7
  • 55
  • 131

2 Answers2

9

currentItem is an optional property of AVPlayer. The following compiles in Swift 4.2/Xcode 10 (note the additional question mark in the key path):

let observer = player.observe(\.currentItem?.status, options: [.new]) {
    (player, change) in
    guard let optStatus = change.newValue else {
        return // No new value provided by observer
    }
    if let status = optStatus {
        // `status` is the new status, type is `AVPlayerItem.Status`
    } else {
        // New status is `nil`
    }
}

The observed property is an optional AVPlayer.Status?, therefore change.newValue inside the callback is a “double optional” AVPlayer.Status?? and must be unwrapped twice.

It may fail to compile in older Swift versions, compare Swift’s ‘observe()’ doesn’t work for key paths with optionals? in the Swift forum.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • There is a warning as well when I try to extract status. Please suggest what is wrong. – Deepak Sharma Sep 24 '18 at 09:05
  • 1
    @DeepakSharma: `newValue` is not a NSNumber. See update. – Martin R Sep 24 '18 at 09:10
  • Ok, final question. How do we remove this observer later. Just setting returned statusObserver = nil? – Deepak Sharma Sep 24 '18 at 09:17
  • 1
    @DeepakSharma: From the Quick Help: *"when the returned NSKeyValueObservation is deinited or invalidated, it will stop observing."* See also https://stackoverflow.com/a/46591915/1187415 or https://stackoverflow.com/a/50216154/1187415 – Martin R Sep 24 '18 at 09:21
  • I tried it. Does not work, always returns nil values. old API still work. Something is wrong. I thought it worked but upon putting breakpoints, only get nil values. – Deepak Sharma Sep 25 '18 at 10:34
  • Your answer may be correct but it may be bug in Swift https://bugs.swift.org/browse/SR-5872 – Deepak Sharma Sep 25 '18 at 10:42
  • 1
    @DeepakSharma: I must admit that I checked only that it compiles, not if it actually works. – Martin R Sep 25 '18 at 10:57
  • Which means there is no way to switch for the time being. Swift sucks, language still evolves and keeps changing with bugs. – Deepak Sharma Sep 25 '18 at 11:02
  • where is documentation for `player.observe(..)?` and what is this backslash syntax ` player.observe(\.currentItem.status`)? i never saw it. – Awais Fayyaz Feb 14 '19 at 12:48
  • 1
    @AwaisFayyaz: This might be a good starting point: https://developer.apple.com/documentation/swift/cocoa_design_patterns/using_key-value_observing_in_swift. – Martin R Feb 14 '19 at 20:08
0

You can also use the player instance instead of the change(NSKeyValueObservedChange) from the closure to get what you need.



//Declare observation token at class level for managing the observation lifetime.

    var kvoToken: NSKeyValueObservation?

//Add your observer

    kvoToken = player.observe(\.currentItem?.status) { (player, _) in
                switch player.currentItem?.status {
                case.readyToPlay:
                    print("Ready to play")
                case .failed:
                    print("failed")
                case .unknown:
                    print("unknown")
                default:
                    print("deafult")
                }
            }

//In deinit, invalidate the token.

    deinit {
        kvoToken?.invalidate()
    }