I'm calling a function in a timer that throws the following error:
AppName.CollectionViewController lastActive]: unrecognized selector sent to instance
The app crashes and throws the exception right when the timer is scheduled to fire. Here's the timer and the function it calls:
var timer = NSTimer.scheduledTimerWithTimeInterval(180, target: self, selector: Selector("lastActive:"), userInfo: nil, repeats: true)
}
func lastActive(){
if PFUser.currentUser(){
PFCloud.callFunctionInBackground("registerActivity", withParameters: [:], target: nil, selector: Selector("lastActive:"))
}
}
Is there another way to call the function in the timer? What do I need to put in the "selector:" parameter?
Thanks!
EDIT: Here's the whole class:
import UIKit
let reuseIdentifier = "Cell"
class LobbyCollectionViewController: UICollectionViewController, UICollectionViewDataSource {
var lobbyData:NSMutableArray = NSMutableArray()
override func viewDidAppear(animated: Bool) {
if (!PFUser.currentUser()){
var loginAlert:UIAlertController = UIAlertController(title: "Sign Up / Login", message: "Enter your login information and press the Log In button to log into the app. If you are new, enter in the desired login information and press the Sign Up button.", preferredStyle:UIAlertControllerStyle.Alert)
loginAlert.addTextFieldWithConfigurationHandler({
textfield in
textfield.placeholder = "Username"
})
loginAlert.addTextFieldWithConfigurationHandler({
textfield in
textfield.placeholder = "Password"
textfield.secureTextEntry = true
})
loginAlert.addAction(UIAlertAction(title: "Login", style: UIAlertActionStyle.Default, handler: {
alertAction in
let textFields:NSArray = loginAlert.textFields as NSArray
let usernameTextField:UITextField = textFields.objectAtIndex(0) as UITextField
let passwordTextField:UITextField = textFields.objectAtIndex(1) as UITextField
PFUser.logInWithUsernameInBackground(usernameTextField.text, password: passwordTextField.text){
(user:PFUser!, error:NSError!)->Void in
if (user){
println("Login successful")
} else {
println("Login failed")
}
}
}))
loginAlert.addAction(UIAlertAction(title: "Sign Up", style: UIAlertActionStyle.Default, handler: {
alertAction in
let textFields:NSArray = loginAlert.textFields as NSArray
let usernameTextField:UITextField = textFields.objectAtIndex(0) as UITextField
let passwordTextField:UITextField = textFields.objectAtIndex(1) as UITextField
var user:PFUser = PFUser()
user.username = usernameTextField.text
user.password = passwordTextField.text
user.signUpInBackgroundWithBlock{
(success:Bool!, error:NSError!)->Void in
if !error{
println("Sign Up successful")
}else{
let errorString = error.userInfo["error"] as NSString
println(errorString)
}
}
}))
self.presentViewController(loginAlert, animated: true, completion: nil)
}
}
override func viewDidLoad() {
super.viewDidLoad()
if PFUser.currentUser(){
self.loadData()
PFCloud.callFunctionInBackground("registerActivity", withParameters: [:], target: nil, selector: "block:")
var timer = NSTimer.scheduledTimerWithTimeInterval(180, target: self, selector: Selector("lastActive:"), userInfo: nil, repeats: true)
//(timeInterval: 180, target: self, selector: "registerActivity", userInfo: nil, repeats: true)
}
func lastActive(){
if PFUser.currentUser(){
PFCloud.callFunctionInBackground("registerActivity", withParameters: [:], target: nil, selector: "block:")
}
}
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Register cell classes
self.collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
// Do any additional setup after loading the view.
}
func loadData(){
lobbyData.removeAllObjects()
if PFUser.currentUser(){
PFCloud.callFunctionInBackground("getOnlineUsers", withParameters: [:], target: nil, selector: "block:")
func block(users: NSArray, error:NSError){
if(error != nil){
self.lobbyData = users as NSMutableArray
}
}
//var timer = NSTimer.scheduledTimerWithTimeInterval(180, target: self, selector: "block:", userInfo: nil, repeats: true)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
// MARK: UICollectionViewDataSource
override func numberOfSectionsInCollectionView(collectionView: UICollectionView!) -> Int {
//#warning Incomplete method implementation -- Return the number of sections
return 1
}
override func collectionView(collectionView: UICollectionView!, numberOfItemsInSection section: Int) -> Int {
//#warning Incomplete method implementation -- Return the number of items in the section
return lobbyData.count
}
override func collectionView(collectionView: UICollectionView!, cellForItemAtIndexPath indexPath: NSIndexPath!) -> UICollectionViewCell! {
//let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as UICollectionViewCell
// Configure the cell
var cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as FightCollectionViewCell
let battle:PFUser = self.lobbyData.objectAtIndex(indexPath!.row) as PFUser
return cell
}
And more detailed error:
[AppName.LobbyCollectionViewController lastActive:]: unrecognized selector sent to instance 0x7ffc60c34100
AppName[435:6742] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[AppName.LobbyCollectionViewController lastActive:]: unrecognized selector sent to instance 0x7ffc60c34100'
*** First throw call stack:
(
0 CoreFoundation 0x00000001117343e5 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x0000000113247967 objc_exception_throw + 45
2 CoreFoundation 0x000000011173b4fd -[NSObject(NSObject) doesNotRecognizeSelector:] + 205
3 CoreFoundation 0x00000001116937ec ___forwarding___ + 988
4 CoreFoundation 0x0000000111693388 _CF_forwarding_prep_0 + 120
5 Foundation 0x0000000111b67e94 __NSFireTimer + 83
6 CoreFoundation 0x000000011169c4d4 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20
7 CoreFoundation 0x000000011169c095 __CFRunLoopDoTimer + 1045
8 CoreFoundation 0x000000011165f3cd __CFRunLoopRun + 1901
9 CoreFoundation 0x000000011165e9f6 CFRunLoopRunSpecific + 470
10 GraphicsServices 0x00000001153a69f0 GSEventRunModal + 161
11 UIKit 0x0000000111fbd990 UIApplicationMain + 1282
12 AppName 0x000000010fff808e top_level_code + 78
13 AppName 0x000000010fff80ca main + 42
14 libdyld.dylib 0x00000001137cf145 start + 1
15 ??? 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)
If you use a selector with a ':' at the end, it will look for a selector which takes a parameter. Try with "lastActive" or add a parameter for the sending context's 'self'.
The function marked by the name given to the selector must be a member of the target. I.e. in the code above you specify the target as 'self' which refers to an instance of the class LobbyCollectionViewController. However, your function func lastActive()
is embedded within the override func viewDidLoad()
function - which means it is not a member of LobbyViewController (and is a very non typical way of coding, to have functions within functions, that are not closures). To fix this, refactor as follows:
override func viewDidLoad() {
super.viewDidLoad()
if PFUser.currentUser(){
self.loadData()
PFCloud.callFunctionInBackground("registerActivity", withParameters: [:], target: nil, selector: "block:")
var timer = NSTimer.scheduledTimerWithTimeInterval(180, target: self, selector: Selector("lastActive"), userInfo: nil, repeats: true)
//(timeInterval: 180, target: self, selector: "registerActivity", userInfo: nil, repeats: true)
}
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Register cell classes
self.collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
// Do any additional setup after loading the view.
}
func lastActive(){
if PFUser.currentUser(){
PFCloud.callFunctionInBackground("registerActivity", withParameters: [:], target: nil, selector: "block:")
}
}
Note that the function is now at the same level as viewDidLoad and that the selector is without the ':'. Hope that works for you!