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])
}
Related
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.
This is driving me banerners.
Here's the XCode 6.4 autocomplete from typing assert in Swift:
assert(condition: Bool, message: String)
Here's how I'm using it in my init:
required init (mainController: MainVC) {
assert(mainController.parameterThatShouldNotBeNil != nil,
"oops parameterThatShouldNotBeNil is nil")
super.init()
}
Here's the error message from the little red exclamation point:
Cannot invoke 'assert' with an argument list of type '(Bool, String)'
...has me pulling my hair out. Double You Tee Eff?
EDIT
Here's a short example that can be pasted into XCode:
class HasNoNil {
var definitelyNotNil: String = "nope not nil"
}
class ChecksForANil{
init (objectToCheck: HasNoNil){
assert(objectToCheck.definitelyNotNil != nil, "whoops it actually is nil")
}
}
That gives the same error I'm seeing.
The underlying problem is that Swift can't compare a non-Optional value with nil, basically because that comparison doesn't make sense: it's not Optional, so it can't possibly be nil, and you can work that out at compile time (or rather, there's no overload for != set up between non-Optional and nil, because there doesn't need to be.)
If you try a simpler example without the assert:
class HasNoNil {
var definitelyNotNil: String = "nope not nil"
}
class ChecksForANil{
init (objectToCheck: HasNoNil){
if (objectToCheck.definitelyNotNil != nil) { println("Not nil") }
// assert(objectToCheck.definitelyNotNil != nil, "Hrm")
}
}
...you'll get a more sensible error: "Could not find an overload for '!=' that accepts the supplied arguments". (Xcode 6.2/Swift 1.2) If you make definitelyNotNil an Optional, then everything works fine.
So, I think this is just a misleading error message. Sadly, this type of misleading error, when the problem is with the parameters to a function, seems quite prevalent in early Swift compilers. You might want to try it under Swift 2 and raise a report with Apple if it's not fixed already.
I have Write simple Code here
self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Cancel, target: self, action: Selector("cancelClick"))
Actual Function
func cancelClick(isAlert:String = "yes"){
self.dismissViewControllerAnimated(true, completion: { () -> Void in
if isAlert == "yes" {
Functions.displayAlert("called")
}
})
}
self.cancelClick() - Worked but if i didn't pass the argument
self.cancelClick(isAlert:"no") - Crashed
So what should be my selector if i have to pass argument in default perameter
tried with both Selector("cancelClick") and Selector("cancelClick:") but no luck.
The thing is that the parameter is not up to you. It is always the button (the "sender"), and that is the only thing it can be.
In other words, if you want this function to have a parameter, then by all means you will need to set your selector string as "cancelClick:" - the colon means that it takes a parameter. But that parameter must be the button:
func cancelClick(bbi:UIBarButtonItem?) {
However, you will notice that I have cleverly made this UIBarButtonItem parameter an Optional. Why do you think I did that? Because now you can also call it directly and pass nil:
self.cancelClick(nil)
Thus, cancelClick: now has a way to know whether the call comes from the tapping of a button or by a direct call - if bbi is not nil, the button was tapped; if bbi is nil, we were called directly from code. Sneaky, eh?
Another sneaky approach is to make the parameter an AnyObject:
func cancelClick(sender:AnyObject) {
The beauty of this is that you can call it with any kind of class instance. cancelClick can check the type of the sender. If it is a UIBarButtonItem (sender is UIBarButtonItem), then we were called by tapping the button. Otherwise, if called in code, you can pass in a string or anything else that this function might be prepared to deal with.
Don't use Selector("cancelClick:"), try instead just "cancelClick:" with the colon so you can pass an argument.
And as Matt said, the argument you must pass is the sender (the button) itself.
Given a function reference, is there a way in Swift to get the name of that function as a string suitable for passing to NSSelectorFromString?
I'd like to wrap NSNotificationCenter's addObserver with a version that takes a function reference instead of a selector string:
addObserver(self, function: someCallback, name: "some notification", object: nil)
But addObserver takes a String selector argument.
You're reinventing an unnecessary wheel. NSNotificationCenter already has an observation method that takes a function (what Objective-C calls a "block"):
addObserverForName:object:queue:usingBlock:
So just use that.
I'd still like to find an answer to my original question, but based on #matt's suggestion, I'm currently using this extension:
extension NSNotificationCenter {
class func addObserver(function: (NSNotification!) -> (), name: String? = nil, object: AnyObject? = nil, queue: NSOperationQueue? = nil) {
defaultCenter().addObserverForName(name, object: object, queue: nil, usingBlock: function)
}
}
Since it implicitly uses defaultCenter() and provides defaults for object and queue, which I'd almost always pass nil for, it allows for a more succinct call:
NSNotificationCenter.addObserver(doSomething, name: "some notification")
I like that it links against the actual function (doSomething), rather than a string representation. It's not a general purpose extension, but I think it covers 90% of the cases where I register for notifications.
Edit: I'm leaving this here for interest, but it's way too complicated (got wrapped up in how the question was asked, rather than the goal). Beyond the existing block-based approach, there's also this handy extension to it.
I wouldn't do it this way. It's too limiting because it would exclude function literals (anonymous functions).
Instead, I would play this game:
Create an dictionary property mapping [String: Void -> ()] (string to function)
When you register a new function, make up a unique, random key and store the function you're passed in your dictionary with that key.
Register with the selector observer_dispatcher_<key> (or whatever prefix you like).
Implement resolveInstanceMethod: to dynamically create any observer_dispatcher_ method you're requested. They can all point to the same IMP, something like:
(assuming this is good Swift; I haven't tried it):
void _observer_dispatcher(self: AnyObject, _cmd: Selector) {
let name = // strip the leading stuff off of _cmd
if let f = self.dispatchTable[name] {
f()
}
}
(This is still pretty sloppy; I'll have to think about the correct correct impl more, but this is the direction I'd go in.)
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.