Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-5872] newValue and oldValue always nil when observing AVPlayerItem.status #3807

Open
pwightman opened this issue Sep 12, 2017 · 21 comments
Assignees

Comments

@pwightman
Copy link

Previous ID SR-5872
Radar rdar://problem/35535407
Original Reporter @pwightman
Type Bug

Attachment: Download

Environment

Xcode 9 beta 6

Additional Detail from JIRA
Votes 12
Component/s Foundation
Labels Bug
Assignee @Catfish-Man
Priority Medium

md5: 6608d02d7d6a7a0859380d1221f9fab8

relates to:

  • SR-14210 oldValue always nil when observing AVQueuePlayer.currentItem
  • SR-11617 newValue and oldValue always nil when observing AVPlayerItem.duration

Issue Description:

Using the new observe KVO stuff in Swift 4 doesn't seem to be working for `AVPlayerItem.status`, as `change.newValue` and `change.oldValue` are always nil (note: the `status` property is not an optional).

let item = AVPlayerItem(url: URL(string: "https://google.com")!)

item.observe(\.status, options: [.old, .new, .initial]) { (item, change) in
    print(change.newValue) // always nil
}

I noticed that observing this value using the old `addObserver` calls does yield values for `change?[.newKey]` and `change?[.oldKey]`, but they are not `AVPlayerItemStatus` values, they are integers. Which makes sense. But that makes me wonder if perhaps internally, the new KVO system is trying to cast the integer as an `AVPlayerItemStatus`? If so, it seems this would be a generic problem to all enums of a similar nature?

See attached playground (it just contains the above code).

@belkadan
Copy link

cc @Catfish-Man

@swift-ci
Copy link
Contributor

Comment by Christopher Fuller (JIRA)

I just tested this in Xcode 9.2 and experience the same issue.

This is the test code I used:

import PlaygroundSupport
import AVFoundation

PlaygroundPage.current.needsIndefiniteExecution = true

let item = AVPlayerItem(url: URL(string: "https://ia802508.us.archive.org/5/items/testmp3testfile/mpthreetest.mp3")!)

print("initial status:", item.status.rawValue)

item.observe(\.status, options: [.new, .old, .initial]) { _, change in
    print("status:", change.newValue ?? "nil")
}

let player = AVPlayer(playerItem: item)
player.play()

DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
    print("final status:", item.status.rawValue)
    PlaygroundPage.current.finishExecution()
}

Output:

initial status: 0
status: nil // <- should not be nil
final status: 1

@swift-ci
Copy link
Contributor

Comment by Christopher Fuller (JIRA)

This next test utilizes a custom enumeration, instead of AVPlayerItem status, but still nil 🙁

import Foundation

@objc enum Status: Int {
    case closed, open
}

class Example: NSObject {
    @objc dynamic var status: Status = .closed
}

let example = Example()

print("initial status:", example.status.rawValue)

example.observe(\.status, options: [.new, .old, .initial]) { _, change in
    print("status:", change.newValue ?? "nil")
}

example.status = .open

print("final status:", example.status.rawValue)

Output:

initial status: 0
status: nil // <- should not be nil
status: nil // <- should not be nil
final status: 1

@belkadan
Copy link

@swift-ci create

@swift-ci
Copy link
Contributor

swift-ci commented Apr 7, 2018

Comment by Holger Wiedemann (JIRA)

I can't believe this bug still exists in the Swift 4.1 Core delivered with Xcode 9.3 (9E145).

It's pretty obvious why enums don't work with the block based KVO method if you look at the Foundation code

let notification = NSKeyValueObservedChange(kind: change.kind,
                                            newValue: change.newValue as? Value,
                                            oldValue: change.oldValue as? Value,

Trying to cast a NSNull / NSValue / NSNumber to an enum type will always fail since enums currently do not implement the _ObjectiveCBridgeable protocol.

The worst thing about this is that the cast from the change dictionary entry silently fails without any warnings or errors. The value simply becomes nil which may cause all sorts of undefined behavior if you don't know about this bug.

@jckarter
Copy link
Member

jckarter commented Jun 6, 2018

cc @Catfish-Man, from wiedem (JIRA User)'s description it sounds like this is hopefully a small fix in the overlay?

@Catfish-Man
Copy link
Member

Seems very plausible. The main issue is just that I’ve been heads-down on iOS performance work and haven’t had time for overlay stuff. If someone wants to grab this, please feel free, otherwise I’ll see if I can carve out some time.

@lilyball
Copy link
Mannequin

lilyball mannequin commented Sep 5, 2018

It should be fairly easy to fix this for the case where Value: RawRepresentable (I just fixed that in PMKVObserver), but it's not clear to me how to fix this for the case where the value is an optional that wraps a RawRepresentable (which you'd get by observing e.g. \AVPlayer.currentItem?.status).

@lilyball
Copy link
Mannequin

lilyball mannequin commented Sep 6, 2018

Ok I managed to fix it in PMKVObserver for the double-optional case by use of a helper protocol. I feel like there should be a better way but if there is, I'm not sure what.

@ffried
Copy link
Contributor

ffried commented Nov 26, 2018

I've just opened apple/swift#20757 for this.

@swift-ci
Copy link
Contributor

swift-ci commented Mar 5, 2019

Comment by Christopher Fuller (JIRA)

Thanks @ffried for working on this!

@swift-ci
Copy link
Contributor

swift-ci commented Mar 5, 2019

Comment by Christopher Fuller (JIRA)

I see there are some comments on @ffried's PR but it is not merged yet. I originally commented on this issue more than a year ago when I first encountered the bug. Any chance we can get attention on this to get it resolved? I would be so very appreciative!

@swift-ci
Copy link
Contributor

swift-ci commented Jul 6, 2020

Comment by Mike Ciesielka (JIRA)

This report has gone fairly stale, but I can still reproduce this in Xcode 11.5. As the post mentions, this makes it particularly troublesome to use the block-based KVO apis with AVPlayer.

@ffried
Copy link
Contributor

ffried commented Jul 6, 2020

maciesielka (JIRA User) Yea, so has the PR. I've kept it up to date (and it's still conflict-free), but since it adds new public APIs (even if those are just overloads), it has to go through some kind of internal review... And apparently that's where this has gone stale.

@swift-ci
Copy link
Contributor

swift-ci commented Jul 6, 2020

Comment by Mike Ciesielka (JIRA)

No problem, thanks for working on this @ffried and for bumping the PR as well!

@swift-ci
Copy link
Contributor

Comment by Frederick Kellison-Linn (JIRA)

I just encountered this issue and managed to track down this bug. Hope to see it resolved soon!

@swift-ci
Copy link
Contributor

swift-ci commented Nov 5, 2021

Comment by Michael Kaye (JIRA)

Just wasted the afternoon updating my code to use the use observe KVO approach to find out this doesn't work.

Is there any reason this isn't being fixed?

@pwightman
Copy link
Author

Maybe it’s considered a “breaking change” at this point (also able to reproduce in Xcode 13.1)…you might try Combine-based KVO, as it doesn’t seem to have this problem. wuf810 (JIRA User)

let cancellable = item.publisher(for: \.status).sink { status in
    print(status)
}

@swift-ci
Copy link
Contributor

swift-ci commented Nov 5, 2021

Comment by Michael Kaye (JIRA)

Thanks @pwightman I'll look into that.

@ffried
Copy link
Contributor

ffried commented Nov 16, 2021

Thanks @pwightman! I've been meaning to have a look at how Combine solves this and found they worked around this issue in Combine by always getting the current value in the observation callback.
Nevertheless, I've just opened a new PR in swift-corelibs-foundation (where the overlay lives nowadays). This would fix this w/o adding new public API. I just wasn't able to test it locally yet.

@swift-ci
Copy link
Contributor

Comment by Michael Kaye (JIRA)

Parker Wightman Thanks for the suggestion to use Combine. Working brilliantly for me. Anyone else coming here looking for a solution, then combine is it. Some example code:

    let cancellable = player.currentItem?.publisher(for: \.status).sink { status in
      print("AVPlayer Status Change: \(status)")
    }
        
    let cancellable = player.publisher(for: \.timeControlStatus).sink { timeControlStatus in
      print("AVPlayer TimeControlStatus Change: \(timeControlStatus)")
    }
 

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@shahmishal shahmishal transferred this issue from apple/swift May 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants