Swift Observer (KVO): Checking for Existence
Asked Answered
A

4

9

I have a box that the user is able to pan around. For that, I add an observer to check if it's center has changed:

self.boxView!.addObserver(self, forKeyPath: "center", options: .old, context: &BoxCenterContext)

This gets added after an animation that presents the box.

When the box dismisses, I remove it as this:

self.boxView!.removeObserver(self, forKeyPath: "center", context: &BoxCenterContext)

Issue

There is a possibility of the user being able to dismiss the box before box presentation has been completed, ie. before the KVO is added.

When that happens, the app crashes trying to remove a KVO that does not exist.

Question

Is there a way to check for existence of KVO (before trying to remove)?

Amyl answered 11/9, 2018 at 12:34 Comment(0)
T
17

observationInfo property is set when there is an observer added

if self.boxView!.observationInfo != nil {

   self.boxView!.removeObserver(self, forKeyPath: "center", context: &BoxCenterContext) 
}
Thor answered 11/9, 2018 at 12:53 Comment(6)
This seems to work since there is only one observer.Amyl
and it will work for many also , the property is set to nil only when there are zero observersThor
I agree with @Gizmodo, if you register several obvserver this will be not nil and therefore you will not be able to tell if the one you want to remove is really registeredVassal
@Sh_Khan I commented out my avplayer observers (it has no observers), then used this and I got a crash saying the avplayer was never registered as an observer. I checked the vc and no where else are there any observers registered. I printed out the value beforehand and something was returned: print("observationInfo: (player!.observationInfo as Any)") which prints observationInfo: Optional(0x0000600002507a60)Tiedeman
But wouldn't this catch ANY observations? What if you want to know if the object is observing a particular notification?Gypsy
This is not sufficient as it only checks to see if there is an observation. If there is and you proceed to try and remove something that is not specifically observed, it will crash.Ridley
R
7

Apple does not provide any API for check existence of observer but you can manage a Bool flag for this. Like when u register KVO you set isObserver bool to true and then before removing observer you need to check isObserver true of false if isObserver is true so remove observer and if it's false don't do any thing.

Renata answered 11/9, 2018 at 13:48 Comment(0)
A
2

Use this extension

extension NSObject {
  func safeRemoveObserver(_ observer: NSObject, forKeyPath keyPath: String) {
    switch self.observationInfo {
    case .some:
      self.removeObserver(observer, forKeyPath: keyPath)
    default:
      debugPrint("observer does no not exist")
    }
  }
}
Arlo answered 29/9, 2019 at 7:43 Comment(0)
H
2

According to Apple doc observer removal code should be wrapped in a @try @catch block, because there is no API to check whether a particular object is an observer. For example (forgive me objective c please):

    @try {
        [self.event removeObserver:self forKeyPath:@"eventTimeZone"];
    } @catch (NSException *exception) {
        NSLog(@"Tried to remove observer from event, but there was no observer anymore.");
    }
Hylan answered 5/1, 2021 at 16:42 Comment(1)
This doesn't work in Swift. Very annoyingly, the 'removeObserver' call is not marked with 'throw', so it is impossible by fundamental language design to catch an exception from it.Diversified

© 2022 - 2024 — McMap. All rights reserved.