I have a function with one parameter. This parameter has a default value. (true)
func disableButton(animated: Bool = true) {
if (animated) {
...
} else {
...
}
}
So i can call the function as:
disableButton(animated: true)
or:
disableButton()
and they give me the same result.
Now I have an NSTimer running a selector on completion like this:
buttonFadeTimer = NSTimer.scheduledTimerWithTimeInterval(NSTimeInterval(1.5), target: self, selector: Selector("disableButton"), userInfo: nil, repeats: false)
It will send the disableButton() message on completion. However on completion it causes the app to crash.
If i do:
func disableButton() {
disableButton(animated: true)
}
Then the timer successfully sends that message and the app does not crash.
Of course this is really ugly and kind of limits the great Swift feature of default parameters.
Is this a bug (that I should report) or am I doing it wrong?
Thanks for any help!
There's a difference between -disableButton and -disableButton: selectors. But you can't just use your desired selector like this:
buttonFadeTimer = NSTimer.scheduledTimerWithTimeInterval(NSTimeInterval(1.5), target: self, selector: Selector("disableButton:"), userInfo: nil, repeats: false)
because NSTimer here assumes selector with no arguments or selector with single argument (which will be used to pass firing NSTimer instance).
I believe, you should migrate from using selectors, as you want to use all that great features Swift delivers, as far as selectors is outdated pattern.
Just a note: it could work if you use "disableButton:" selector, maybe NSTimer's pointer will be interpreted as true and maybe not, not sure what will happen (maybe it could crash due to Swift strong typing stuff). But depending on that behaviour is a bad practice which could lead to bugs which are extremely hard to exterminate.
Related
I'm trying to set up a timer to run an event periodically, as described here. I get that the hashtag parameter has been deprecated, so I tried to rewrite the startTimer code accordingly:
func startTimer()
{
let theInterval: NSTimeInterval = 5.0
self.timer = NSTimer.scheduledTimerWithTimeInterval
(
interval: theInterval,
target: self,
selector: Selector("timerTick:"),
userInfo: "Hello!",
repeats: true
)
}
Problem is, I keep getting the error: "Ambiguous reference to member 'scheduledTimerWithTimeInterval( _:invocation:repeats:)'". But I'm not trying to run scheduledTimerWithTimeInterval( _:invocation:repeats:), I'm trying to run scheduledTimerWithInterval:target:selector:userInfo:repeats. I'd think that would be obvious from the parameters I'm passing.
What do I need to do differently?
There are two problems:
It is confused by the newline and whitespace between scheduledTimerWithTimeInterval and the open parentheses.
You should not supply the first parameter label.
So, you could do:
timer = NSTimer.scheduledTimerWithTimeInterval(
2.0,
target: self,
selector: #selector(timerTick(_:)),
userInfo: "Hello!",
repeats: true
)
Note, I also replaced the Selector("timerTick:") with the #selector syntax.
I've read the other posts. I've read the docs. I've read nearly everything I can get my hands on in regards to this issue, but it persists. To be clear, this problem ONLY happens if I want to capture the timer as a parameter in the receiving function.
Here's the error:
2016-03-07 18:26:29.000 AlarmClockAI[12286:3807622] -[AlarmClockAI.Alarm playAlarm]: unrecognized selector sent to instance 0x7fd0936289e0
I have a class derived from NSObject. In that class, I'm creating a series of timers like so:
timer = NSTimer(fireDate: fireDate, interval: 5.0, target: self, selector: Selector("playAlarm:"), userInfo: ["UUID":UUID], repeats: false)
NSRunLoop.currentRunLoop().addTimer(timer!, forMode: NSDefaultRunLoopMode)
func playAlarm(timer: NSTimer) {}
I still get the unrecognized selector error.
Other variations I've tried:
NSTimer(fireDate: fireDate, interval: 5.0, target: self, selector:"playAlarm:", userInfo: ["UUID":UUID], repeats: false)
Same error.
Other notes:
I'm running Swift 2.1.1
I realize I shouldn't need the #objc tag, but I've tried that too.
Clean + build + restart didn't help.
FML
Ugh. Sorry guys. I found the answer: I have more than one method that creates timers, and I was looking at the wrong one.
I was working on a game with Swift 1 and Xcode 6 and everything was working perfectly. But when I updated to XCode 7 and Swift 2, I was getting a weird issue. In my game I had some NSTimers saved in variables to spawn the enemies. An example of one of this variables was something like this:
var firstTimer = NSTimer.scheduledTimerWithTimeInterval(actualTime, target: self, selector: Selector("SpawnEnemy"), userInfo: nil, repeats: true)
When I updated, every variable of this type (I had like 4) got an issue that says: "Initialization of variable "firstTimer" was never used; consider replacing with assignment to "_" or removing it."
I can't just change every variable's name to "_", and I never had to initialize it.
What is going on? And why didn't this happened before?
btw: I do use this variables in the code.
The error says it all, you didn't use the value in your code at all. If you intend to never stop the timer, you can just omit the declaration of the variable like this:
NSTimer.scheduledTimerWithTimeInterval(actualTime, target: self, selector: Selector("SpawnEnemy"), userInfo: nil, repeats: true)
But if you want to stop it later you should probably save it:
class MyViewController {
var timer : NSTimer?
func startTimer() {
timer = NSTimer.scheduledTimerWithTimeInterval(actualTime, target: self, selector: Selector("SpawnEnemy"), userInfo: nil, repeats: true)
}
func stopTimer() {
timer?.invalidate()
}
}
What you can do:
var timer = ... gives a warning that you didn't use the value (if you didn't) and a warning that timer never changed and should be a let.
let timer = ... gives a warning that you didn't use the value (if you didn't)
let _ = ... same as _ = ... evaluates the right-hand side and discards the result. No warnings
Just ... does the same as the last one, but can give a warning on functions with the #warn_unused_result attribute when the result isn't used.
Say I have a textfield - inputTextFieldand a button on UI with action performActionDynamically, in same class I define two functions: 1. firstFunc, 2. secondFunc, now I want to achieve this behavior:
If user types "firstFunc" in textfield and then he taps on button it should invoke firstFunc function, and if he types "secondFunc" in textfield and then he taps on button it should invoke secondFunc function.
In objective-c I would have easily achieved it by following below pseudocode:
Within performActionDynamically pass inputTextField.text in NSSelectorFromString() to obtain selector
Invoke performSelector:withObject: on self to perform respective function
The only thing which I can think in implementing same behavior in swift is -
Define two closures with name firstFunc, secondFunc
Store the closures in a dictionary as values for keys - 'firstFunc' and 'secondFunc' respectively
Obtain respective closure from dictionary based on value in textfield
Invoke obtained closure
Is there any better way to achieve intended behavior? Please guide.
You could try something like this:
if self.respondsToSelector(Selector(selectorString)) {
NSTimer.scheduledTimerWithTimeInterval(NSTimeInterval(0), target: self, selector: Selector(inputTextField.text), userInfo: nil, repeats: false)
}
This is done as a callback, so you will have to take that into account.
You could use NSThread instead of NSTimer if you prefer:
NSThread.detachNewThreadSelector(Selector(inputTextField.text), toTarget: self, withObject: nil)
Get the compile error message from xCode with Swift language: "extra argument userinfo in call". the question is how to use userInfo data from timer to argument "userInfo" in notification center.
func onSetAudioWithTrackIndex(CurrentTrackIndex:Int, urlAudio: String){
........
//try to pass currentTrackIndex data to timerfire
playTimeClock = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: "onSetPlayProgressTime:", userInfo: CurrentTrackIndex, repeats: true)
}
//Timerfire
func onSetPlayProgressTime(timer: NSTimer){
var currentTime = mediaPlayer.currentPlaybackTime
var playDuration = mediaPlayer.duration
var trackIndexDict = ["trackIndex" : timer.userInfo]
................
if currentTime == playDuration{
NSNotificationCenter.defaultCenter().postNotificationName(MPMoviePlayerPlaybackDidFinishNotification, object: self, userInfo: trackIndexDict)
}
return
}
Swift can give you slightly strange compiler errors sometimes, which amount to “there were multiple possible options, none of them worked, so here’s what’s wrong with one of them”. When the one it complains about is not the one you were trying for, this can be very strange
This is why Swift tells you so often that something is “not a Int8” even though that has nothing to do with what you were trying to do (huh, I was trying to concatenate two strings, what have Int8s got to do with anything? – it’s because one of the possible options for the + operator is to work on Int8s so that’s the one it chooses to complain about).
In this case, postNotificationName has multiple overloaded versions, one with 1 argument, one with 2, and one with 3 (the one you want). None of them fit the types you’ve supplied so it’s saying “one of the options is a call with 2 arguments, and you supplied 3, so that won’t work, there’s an extra argument”.
Unfortunately this is really quite confusing and throws you off the scent of what is actually wrong. Assuming you cut and pasted your actual code, looks like there’s a spelling error in MPMoviePlayerPlaybackDidFinishNotification, and the userInfo argument label is missing a colon after it.
(p.s. you don’t need to explicitly put a return at the end of functions that return void, though it doesn’t do any harm)
In swift 3 , I got this same error. When I was converting the swift 2.2 to swift 3, as the syntax was changed so it throws this error.
Swift 3:
NotificationCenter.default.post(name: NSNotification.Name(rawValue: MPMoviePlayerPlaybackDidFinishNotification), object: self, userInfo: trackIndexDict)
The problem is
that the userInfo property of NSTimer is an optional:
var userInfo: AnyObject? { get }
so that trackIndexDict has the type [String : AnyObject?] which is not convertible
to [NSObject : AnyObject] as expected by the last parameter of postNotificationName().
With optional binding you can "test and unwrap" the property:
if currentTime == playDuration {
if let timerInfo: AnyObject = timer.userInfo {
let trackIndexDict = ["trackIndex" : timerInfo]
NSNotificationCenter.defaultCenter().postNotificationName(MPMoviePlayerPlaybackDidFinishNotification,
object: self,
userInfo: trackIndexDict)
}
}
thanks, Martin, this(userInfo of NSTimer is optional) is root cause. with the below change. this can be fixed
if let timerUserInfo: AnyObject = timer.userInfo! {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: MPMoviePlayerPlaybackDidFinishNotification), object: self, userInfo: ["trackIndex":timerUserInfo])
}