Search code examples
iosswiftswift2key-value-observing

AVPlayer removing observer crash in Swift 2.2


I have a video app that I built a while back in Swift 1 and I've been trying to migrate to Swift 2.2. It all (finally) works apart from a weird crash to do with observers.

func removeObservers()
{
    print("REMOVING OBSERVERS")
    if ( !self.is_image && self.player != nil  ) {
        if (self.player?.observationInfo != nil) {

            self.player?.removeObserver(self, forKeyPath: "currentItem.status")
            self.player?.removeObserver(self, forKeyPath: "readyForDisplay")

        }
    }
    NSNotificationCenter.defaultCenter().removeObserver(self)

}

This worked previously using SwiftTryCatch but with the lines in place crashes with "'Cannot remove an observer for the key path "readyForDisplay" from because it is not registered as an observer.'" OR with that an observer is registered on a deallocated object if I comment it out.

If I add a do { } catch {} to it I get an error that "this does not throw" and it just crashes the same. How do I go about putting this in some form of try-catch format?


Solution

  • In Swift 2, the libs got annoyingly strict about errors that are truly unexpected (which throw) versus errors that the programmer could have prevented (which do not throw, but just crash your app).

    (I’m not a fan of this distinction, or at least not of all the specific decisions Apple made about which errors fall in which category. The JSON API verges on the nonsensical in this department. But…we work with the API we’ve got.)

    The NSKeyValueObserving docs say:

    It is an error to call removeObserver:forKeyPath: if the object has not been registered as an observer.

    “It is an error” is Apple code for “you are responsible for never doing this, and if you do, your app will crash in an uncatchable way.”

    In these situations, there is usually an API call you can make to check the validity of the thing you’re about to do. However, AFAIK, there’s no KVO API call you can make to ask, “Is X observing key path Y of object Z?” That means you have three options:

    • Figure out why you’re trying to remove an observer from something you’re not observing, and prevent that using your program’s own internal logic.
    • Keep a weak instance var for “player I’m observing,” and check that for a match before attempting to remove the observer.
    • Add self as an observer before removing it. (I’m pretty sure that a redundant add is OK.)