unrecognized selector sent to instance in ViewController using Notifications - ios

I've been looking around for a fix, but couldn't seem to fix it by myself.
I'm trying to sent notifications through my controller - model. I'm getting an error on my appdelegate saying:
[Test.ViewController naamInModelChangedHandler]: unrecognized selector
sent to instance 0x7f81c85006c0
I'm sending my notification in my viewDidLoad like this:
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: "naamInModelChangedHandler",
name: "NAAM_CHANGED",
object: model)
In my ViewController I made a function like this:
func naamInModelChangedHandler ( notification:NSNotification ) {
println("De naam in de model is veranderd naar \(model.naam!)")
NSNotificationCenter.defaultCenter().removeObserver(
self,
name: "NAAM_CHANGED",
object: model)
}
And this is how my model looks like (but I don't think this has anything to do with it? :
var naam: String? {
didSet {
NSNotificationCenter.defaultCenter().postNotificationName("NAAM_CHANGED", object: self)
}
Anyone who could help me out fixing this error?

The name of the selector should be "naamInModelChangedHandler:". Note the : since naamInModelChangedHandler takes an NSNotification as an argument. Therefore, you should add the observer like so:
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: "naamInModelChangedHandler:",
name: "NAAM_CHANGED",
object: model)

Related

Trouble adding observers inside an actor

I want to change a value in an actor when the application enters the background. Normally, I'd do this:
NotificationCenter.default.addObserver(
self,
selector: #selector(applicationDidEnterBackground), // error 1
name: UIApplication.didEnterBackgroundNotification, // error 2
object: nil
)
#objc private func applicationDidEnterBackground() { // error 3
foo = true
}
However, I can't do this because it gives me the errors:
Argument of '#selector' refers to instance method 'applicationDidEnterBackground()' that is not exposed to Objective-C
Main actor-isolated class property 'didEnterBackgroundNotification' can not be referenced on a non-isolated actor instance
Actor-isolated instance method 'applicationDidEnterBackground()' cannot be #objc
How can I do this within my actor? Or will I need to have a wrapper to my actor that observers didEnterBackgroundNotification and calls a method on my actor?

How to properly fire/call a "selector" in Swift?

Question Summary:
If you have a Swift class that takes a selector as an argument in its initializer, how do you manually "fire/call" that selector?
Full Question:
Consider the following attempt at making a custom timer in Swift:
let TIME_INTERVAL = 0.1
class ValueAnimator : NSObject {
private var timer = Timer()
private let maxRep: Int
private var currentRepIndex: Int = 0
private var selector: Selector
init(durationInSeconds: Int, selector: Selector) {
print("VALUEANIMATOR INIT")
self.maxRep = Int(Double(durationInSeconds) / TIME_INTERVAL)
self.selector = selector
}
func start() {
timer = Timer.scheduledTimer(timeInterval: TIME_INTERVAL, target: self, selector: (#selector(timerCallback)), userInfo: nil, repeats: true)
}
#objc func timerCallback() {
currentRepIndex += 1
perform(selector) // <-------- this line causes crash, "unrecognized selector sent to instance 0x600001740030"
print ("VA timer called!, rep: \(currentRepIndex)")
if currentRepIndex == maxRep {
timer.invalidate()
print("VA timer invalidated")
}
}
}
The usage of this "ValueAnimator" would be similar to a normal Timer/NSTimer, in that you pass a "selector" as an argument and that selector is called each time the ValueAnimator fires:
[In Parent Class]:
// { ...
let valueAnimatorTest = ValueAnimator(durationInSeconds: 10, selector: #selector(self.temp))
valueAnimatorTest.start()
}
#objc func temp() {
print("temp VA callback works!") // this doesn't happen :(
}
I'm trying to implement the same thing and as I understand, the line:
perform(selector)
should fire the selector in the parent class, but instead I get the error: "unrecognized selector sent to instance 0x600001740030"
I'm in a bit over my head here. I have tried googling the error, but everyone seems to be talking about how to use a selector from the parent-side (how to use Timer.scheduledTimer(), etc.) but I already know how to do that successfully.
I've also tried various tweaks to the code (changing public/private, scope of variables, and different forms of the performSelector() function)... but can't figure out the proper way to make the selector fire... or the unrelated mistake I've made if there is one.
Thanks for any help.
By calling perform(selector) it's like you're calling self.perform(selector) (self is implied), and by doing so the current instance of the ValueAnimator class is the object that actually performs the selector. When that happens, it tries to call a method called temp() of the ValueAnimator class, but as it doesn't exist the app is crashing.
You can verify that if you add a temp() method in the ValueAnimator:
#objc func temp() {
print("Wrong method!!!")
}
If you run now you'll have no crash and the "Wrong Selector!!!" message will appear on the console.
The solution to your problem is to pass the object that should run the selector method along with the selector to the initialisation of the ValueAnimator object.
In the ValueAnimator class declare the following property:
private var target: AnyObject
Update the init method so it can get the target as an argument:
init(durationInSeconds: Int, selector: Selector, target: AnyObject) {
...
self.target = target
}
Also update the timerCallback():
#objc func timerCallback() {
...
_ = target.perform(selector)
...
}
Finally, when you initialise a ValueAnimator instance pass the object that the selector belongs to as well:
let valueAnimatorTest = ValueAnimator(durationInSeconds: 10, selector: #selector(self.temp), target: self)
Run again and the proper temp() method will be executed this time.
I hope it helps.
You are calling perform on the wrong object: its an instance method of NSObject, so you are trying to call perform on ValueAnimator and ValueAnimator does not respond to "temp". You must pass in both the object and the selector you want to perform, then you call perform on that object with the selector. Notice that this is exactly what Timer does: you have to pass in self as the object and the timer call the selector you specify on self.

NotificationCenter addObserver(observer:selector:name:object) -- what is object?

I am having trouble understanding what the object parameter is in NotificationCenter.default.addObserver(observer:selector:name:object)
If I understand it correctly, it acts as a kind of filter; only notifications posted from this object will be observed. But I can't seem to actually figure out how to use it.
I created a class and made a global instance of it
class FooClass {
func postNotification() {
NotificationCenter.default.post(name: NSNotification.Name("TestNotification"), object: self)
}
}
let globalFoo = FooClass()
Then in my first ViewController I press a button which calls globalFoo.postNotification()
Then in my second ViewController I registered like so:
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(notificationReceived), name: NSNotification.Name("TestNotification"), object: globalFoo)
}
func notificationReceived() {
print("notification received")
}
}
It works fine when I don't specify object (i.e. nil), so clearly I'm misunderstanding what it is.
The object parameter used when posting a notification is to indicate what object is actually posting the notification.
When adding an observer, you can leave object nil and you will get all of the named notifications regardless of which object actually sent the notification. Or you can specify a specific object when adding an observer and you will then only be notified when that specific object posts the named notification.
Some notifications use this parameter to provide more appropriate information to the observer.
For example, notifications like NSManagedObjectContextObjectsDidChange optionally accepts NSManagedObjectContext object so that it can notify changes only from that context.

Swift - unrecognized selector sent to instance

I have a simple NSNotification set up on my Swift project.
NSNotificationCenter.defaultCenter().addObserver(self, selector: "serviceAccessChanged", name:"LocationAccessChangedNotification", object: nil)
I've also tried...
NSNotificationCenter.defaultCenter().addObserver(self, selector: "serviceAccessChanged:", name:"LocationAccessChangedNotification", object: nil)
The method called looks like so.
private func serviceAccessChanged() {
println("serviceAccessChanged")
}
When the notification is made I receive the following error.
-[CoolApp.HomeViewController serviceAccessChanged]: unrecognized selector sent to instance 0x7fc91324bba0
What's wrong and how do I fix this?
Private functions are not exposed to Objective C, and this is why you get this exception. Make this method accessible, and use the serviceAccessChanged selector.
Just mark your private method with #objc
#objc private func serviceAccessChanged() {
println("serviceAccessChanged")
}

NSNotification cannot find selector

I'm posting a notification via the default center, like so:
NSNotificationCenter.defaultCenter().postNotificationName(ColorDidGetTappedNotification, object: self)
I'm observing the notification in another instance like so:
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("colorDidGetTapped:"), name: ColorDidGetTappedNotification, object: nil)
....
func colorDidGetTapped(notification: NSNotification) {
println("Notification recieved")
}
But I get an unrecognized selector exception:
UPDATE 1
*** NSForwarding: warning: object 0x7d564490 of class '_D.GameModel' does not implement methodSignatureForSelector: -- trouble ahead
Unrecognized selector -[_D.GameModel colorDidGetTapped:]
Check GameModel is NSObject's subclass
class GameModel: NSObject {
}
Try to observe the Notification using this method: addObserverForName(_:object:queue:usingBlock:). Instead of a selector you pass a block

Resources