Ryan Ryan - 4 months ago 118
iOS Question

Swift - Unrecognized selector sent to instance - I have included a : and arguments align

I am using an NSTimer to send a message on an interval. Here is the code :

{
// ....
var params : [String] = []
params.append(conversion)
params.append(message)
let timer = NSTimer(fireDate: date, interval: 60, target: self, selector: Selector("importTextMessage.sendMessage:"), userInfo: params, repeats: true)

NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)
// ...
}


func sendMessage(params: [String]){ ...}


I have also tried changing to the Swift 2.2 syntax:

let timer = NSTimer(fireDate: date, interval: 60, target: self, selector: #selector(importTextMessage.sendMessage(_:)), userInfo: params, repeats: true)


but that does not change anything.

From every other question posted about the "Unrecognized selector sent to instance", the response is "include a colon in the Selector so that it knows to grab arguments from UserInfo", but I have included that, and can't figure out what is wrong.

What to Note:

The parameters for the function and the parameters passed in through NSTimer userInfo DO match up. They are both arrays of strings.

If it means anything, the code is failing while sendMessage is being called. It does not actually make it to sendMessage.

I am getting an odd warning saying that "String literal is not a valid objective-c selector"

I tried changing my code for my sendMessage to take the Timer as an argument as 1 user suggested :
func sendMessage(timer: NSTimer){
but that still gives the same error.

Thank you for your help in advance, I do appreciate it.

EDIT: here is the function that runs the timer: //Save all of the data
@IBAction func saveText(sender: AnyObject) {
var phone : Double
var active : Int
var frequency : Double
var message : String
var date : NSDate

phone = Double(currentNumber)!
active = 1
message = myTextView.text
date = myDatePicker.date

switch self.frequency.selectedRowInComponent(0) {
case 0:
frequency = 1
case 1:
frequency = 3
case 2:
frequency = 6
case 3:
frequency = 24
case 4:
frequency = 168
case 5:
frequency = 744
default:
frequency = 8760
}

importTextMessage.seedMessage(phone, active: active, frequency: frequency, message: message, date: date)

print(date)
let conversion : String = "+1" + String(Int(phone))


//importTextMessage.sendMessage("Ryan", to: conversion, message: message)
var params : [String] = []
params.append(conversion)
params.append(message)
//importTextMessage.sendMessage(params)

let timer = NSTimer(fireDate: date, interval: 60, target: self, selector: #selector(importTextMessage.sendMessage(_:)), userInfo: params, repeats: true)
//
NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)

}


EDIT: Exact error mssg:

unrecognized selector sent to instance 0x7ffe6b915460
2016-07-25 13:26:09.092 Harass Your Kate[53524:9000621] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Harass_Your_Kate.AddMessageViewController sendMessage:]: unrecognized selector sent to instance 0x7ffe6b915460

Answer

The API of NSTimer is such that the receiver (target function) must take one and only one parameter, of type NSTImer.

https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSTimer_Class/#//apple_ref/occ/clm/NSTimer/scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:

The message to send to target when the timer fires.

The selector should have the following signature: timerFireMethod: (including a colon to indicate that the method takes an argument). The timer passes itself as the argument, thus the method would adopt the following pattern:

  • (void)timerFireMethod:(NSTimer *)timer

In Swift the signature must be

edit: updated to match your code exactly

Note: Swift 2.2 syntax

class TextMessageViewController: UIViewController {
    var importTextMessage: AddMessageViewController // init'd somehow

    override func viewDidLoad() {
        super.viewDidLoad()
        // ...

        // Note how selector matches *the name of the class*
        // As I understand it now, the sendMessage method
        // is declared in the AddMessageViewController class,
        // and importTextMessage is the name of the instance of that class.
        // And we're in a different class now, TextMessageViewController,
        // so using `self` does not make sense here.
        let timer = NSTimer(fireDate: date, interval: 60, target: importTextMessage,
            selector: #selector(AddMessageViewController.sendMessage(_:)), userInfo: params, repeats: true)
        NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)
    }
}


class AddMessageViewController: UIViewController {
    // func must not be private
    func sendMessage(timer: NSTimer) {
       // And now for example
       print("params: \(timer.userInfo)")
    }
}

As far as your userInfo dictionary, that is a property of the timer object, not a parameter to the target function itself.

The SECOND mistake is that the selector syntax is failing to use the name of the class as declared, not the name of some instance variable that has a reference to that class instance. Include more context in your question so it'll get solved faster!

The THIRD mistake is that you're using the wrong class name. If you use self, then the class name in the selector must match the name of the current class. Since you really want the message to go to a different class, you must use that reference instead. See updated code.

I think you still don't fully grasp object references, and their types, and how they must match, and how self is just the reference to the contextual object.

Comments