Swift NotificationCenter remove observer quickest way - ios

I am adding a number of observers in my viewController -- applicationWillResignActive, applicationDidEnterBackground, and many others. I want to remove self as observer to all registered notifications in one line. My question is whether the following line is enough to do that, or are there issues with this code?
deinit {
NotificationCenter.default.removeObserver(self)
}

#Sh_Khan is right:
NotificationCenter.default.removeObserver(self)
You can get even further, as mentioned in the Apple Documentation:
If your app targets iOS 9.0 and later or macOS 10.11 and later, you don't need to unregister an observer in its dealloc method.

So I'm working with this in an app right now and the answer might not be as straightforward.
In the documentation, it does state that for iOS 9 and above you are no longer required to explicitly remove the observer in the deinit/dealloc methods for objects. https://developer.apple.com/documentation/foundation/notificationcenter/1413994-removeobserver
However, it appears that this is only true for selector based notification observers. I'll be referencing this blog post: https://oleb.net/blog/2018/01/notificationcenter-removeobserver/.
If you are using block based observers you must still manually remove the observers.
addObserver(forName:object:queue:using:)
The best general way to do this is to capture the tokens in an array, append them when you add the observer and use them for removal when you deinit/dealloc or otherwise need to remove observer behavior for your object.
in your VC/object properties create an array to store observer 'tokens'
var notifObservers = [NSObjectProtocol]()
Register for a block based notification by capturing the function return object and storing it as a token
let observer = NotificationCenter.default.addObserver(forName: , object: , queue:) { [weak self] notification in
// do a thing here
}
notifObservers.append(observer)
Removal
for observer in notifObservers {
NotificationCenter.default.removeObserver(observer)
}
notifObservers.removeAll()

Yes
NotificationCenter.default.removeObserver(self)
this line is sufficient to remove the vc observation as long as all are added with
NotificationCenter.default.addObserver

Related

Observing UIViewPropertyAnimator is running issue

From Apple Documents pausesoncompletion
Because the completion handler is not called when this property is true, you cannot use the animator's completion handler to determine when the animations have finished running. Instead, you determine when the animation has ended by observing the isRunning property.
But i found observe isRunning not work. util i wathched WWDC 2017 - Session 230-Advanced Animations with UIKit ,i konwed i should observe running
//not work
animator.addObserver(self, forKeyPath: "isRunning", options: [.new], context: nil)
//this work
animator.addObserver(self, forKeyPath: "running", options: [.new], context: nil)
My Question is: where can i find the excatly keypath, not only this case. Thx~
In Swift it's recommended to use the block-based KVO API (available as of Swift 4) which allows you to observe a property in a type safe and compile-time checked manner:
// deinit or invalidate the returned observation token to stop observing
let observationToken = animator.observe(\.isRunning) { animator, change in
// Check `change.newValue` for the new (optional) value
}
Note that the key path is \.isRunning because the property on UIViewPropertyAnimator in Swift is called isRunning.
This both has the benefit that you don't have to know how the string for a given property is spelled and that the changed value has the same type as the observed property.
Note that in Objective-C this API is not available, so the corresponding Objective-C documentation ask you to observe the "running" property. This is because in Objective-C the property is called "running" (but has a getter called "isRunning").
A little late to the party, but the currently accepted answer doesn't exactly answer the question, so let me go ahead.
The discrepancy comes from using different names for ObjC and Swift. Use #keyPath(Type.propertyName)
So for your specific case it would be #keyPath(UIViewPropertyAnimator.isRunning)
Try printing the result. It will correctly show "running"

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?
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.)
Since you are making a call removeObserver(self) at the end of the method, why cant you uncomment above code? Because removeObserver(self) removes all the observers if registered any. I hope this solves your issue.
NSNotificationCenter.defaultCenter().removeObserver(self)
status is a property of either AVPlayer or AVPlayerItem.
readyForDisplay is a property of AVPlayerLayer

AVPlayerItem was deallocated while key value observers were still registered

I am using AVQueuePlayer but it is crashing some times. I am getting this log
AVPlayerItem was deallocated while key value observers were still registered with it.
what is the region for crashing please help me.
If you have added an observer for any key, you have to remove observer. You can do so by adding on the s deinit method
deinit {
<observee>.removeObserver(self, forKeyPath::"<your key>")
}
It seems that you forget to remove observer for your player.
Do you have some code like this? player.addPeriodicTimeObserverForInterval
If so, you need to add this line in deinit method of your controller / object:
player.removeTimeObserver(observer)
Also you maybe forget about this one:
player?.removeObserver(self, forKeyPath: "rate")

How can I setup a listener for SBMediaVolumeChangedNotification?

I have an app that uses the darwin notify center.
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
NULL, // observer
displayStatusChanged, // callback
CFSTR("com.apple.iokit.hid.displayStatus"), // event name
NULL, // object
CFNotificationSuspensionBehaviorDeliverImmediately);
I would really like to capture volume button presses in the background. I found these notifications listed
http://iphonedevwiki.net/index.php/SpringBoard.app/Notifications
Is there a way to trigger methods on SBMediaVolumeChangedNotification?
The SBMediaVolumeChangedNotification is not a Darwin notification, it is a default local notification. To capture it, you would need a common notification observer, like this one (in Swift):
NSNotificationCenter.defaultCenter().addObserver(self, selector: "volumeChanged:", name: "SBMediaVolumeChangedNotification", object: nil)
However, the documentation states that:
The object is an SBMediaController instance.
So I think we would need to pass a SBMediaController instance to the observer, as the object, to make it work. Since the SBMediaController class seems to be inaccessible from the SDK, I don't think there is a way to use that specific notification. I have tried without the object, but it didn't work for me.

mergeChangesFromContextDidSaveNotification Consumes Memory

I have two NSManagedObjectContext, one for ui and one for background tasks. I'm trying to merge changes to UIcontext whenever the background one changed. But whenever I call
mergeChangesFromContextDidSaveNotification:notification
It just start eating memory (will goes up to 1GB on simulator) and cause a crash.
of course I setup a notification:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(coreUpdateFromApp:)
name:NSManagedObjectContextDidSaveNotification
object:nil];
and also tried doing the merge in main thread, etc. No luck!
I found out that URIRepresentation is causing the issue. For some reason it's keep being called. (by apple's code not mine)
Note that I let it run for under a sec and it uses 64.95MB it will grow pretty fast with same call tree. If I keep it running it would crash the osx as well!
The problem is object:nil. You are listening to an endless echo of notifications.
You need to specify a specific context object to listen to notifications from.
The Problem here is, Since Google Analytics also using core-data we are intercepting the endless notifications fired by Google Analytics as well.
Setting object to non-nil value didn't work for me. Found another way and it worked for me as magic.
Inside your observer I have selector method as below
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(managedObjectContextDidSave(_:)), name: NSManagedObjectContextDidSaveNotification, object: nil)
and,
func managedObjectContextDidSave(notification: NSNotification) {
if notification.object?.persistentStoreCoordinator != self.persistentStoreCoordinator {
return
}
//do remaining task here.
}

Resources