I am scheduling a timer that I want to push notifications. The caveat is that my #selector is a func of a singleton as outlined in the code below. However, when the timer is signaled, and the #selector(NotificationManager.instance.pushNotification(timer:)) is called, I get the exception dump outlined below. When I take that same func and place it in the view controller, all is right in the world and no issues. Any ideas?
2017-12-09 20:33:47.439754-0500 Accumoo[3404:2488862] -[MyApp.MainViewController pushNotificationWithTimer:]: unrecognized selector sent to instance 0x107077800
2017-12-09 20:33:47.440181-0500 Accumoo[3404:2488862] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MyApp.MainViewController pushNotificationWithTimer:]: unrecognized selector sent to instance 0x107077800'
*** First throw call stack:
(0x184c89d04 0x183ed8528 0x184c971c8 0x18e47f110 0x184c8f6b0 0x184b7501c 0x18567bd24 0x184c3292c 0x184c32650 0x184c31e50 0x184c2fa38 0x184b4ffb8 0x1869e7f84 0x18e1242e8 0x104b3f624 0x18467256c)
libc++abi.dylib: terminating with uncaught exception of type NSException
Here's the code...
In my View Controller...
// Send user a local notification if they have the app running in the background...
let userInfo = ["title" : "Title", "body" : "Body", "timeIntervalSeconds" : Double(0), "repeats" : false] as [String : Any]
Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(NotificationManager.instance.pushNotification(timer:)), userInfo: userInfo, repeats: false)
In My NotificationManager Class...
class NotificationManager /*: PushNotificationProtocol*/ {
static let instance = NotificationManager()
private init() {
}
@objc public func pushNotification(timer:Timer) {
guard let userInfo = timer.userInfo as? Dictionary<String, Any> else { return }
let title = (userInfo["title"] as? String).valueOrDefault()
let body = (userInfo["body"] as? String).valueOrDefault()
let timeIntervalSeconds = (userInfo["timeIntervalSeconds"] as? Double) ?? 0
let repeats = (userInfo["repeats"] as? Bool) ?? false
self.pushNotification(sender: nil, title: title, body: body, timeIntervalSeconds: timeIntervalSeconds, repeats: repeats)
}
public func pushNotification(sender:Any?, title:String, body:String) {
self.pushNotification(sender: sender, title: title, body: body, timeIntervalSeconds: 0, repeats: false)
}
public func pushNotification(sender:Any?, title:String, body:String, timeIntervalSeconds:Double, repeats:Bool) {
// Center
let center = UNUserNotificationCenter.current()
// Content...
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = UNNotificationSound.default()
// Trigger
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: timeIntervalSeconds, repeats: repeats)
// Push it...
// Does this need to be unique?
let identifier = "NotificationManagerIdentification"
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
center.add(request, withCompletionHandler: { (error) in
if let error = error {
print("Something went wrong: \(error)")
}
})
}
}
** * UPDATE * **
It turns out the call to Timer.scheduleTimer needed to be coded like this (see @maddy's answer):
// Send user a local notification if they have the app running in the background...
let userInfo = ["title" : "Title", "body" : "Body", "timeIntervalSeconds" : Double(10), "repeats" : false] as [String : Any]
Timer.scheduledTimer(timeInterval: 0.5, target: NotificationManager.instance, selector: #selector(NotificationManager.instance.pushNotification(timer:)), userInfo: userInfo, repeats: false)
You are passing the wrong target to your timer.
Change:
Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(NotificationManager.instance.pushNotification(timer:)), userInfo: userInfo, repeats: false)
to:
Timer.scheduledTimer(timeInterval: 0.5, target: NotificationManager.instance, selector: #selector(NotificationManager.instance.pushNotification(timer:)), userInfo: userInfo, repeats: false)
Remember, the target needs to an instance of the class that actually implements the selector to specify. You are passing self
which is the view controller.