Handle the selector parameter in swift - ios

Let's say I declare a function in my CustomTimer class:
class CustomTimer {
class func scheduledTimerWithSelector(aSelector: Selector) -> CustomTimer {
// aSelector ??
}
}
How can I handle this aSelector parameter?
Like the NSTimer.scheduledTimerWithTimeInterval method, how dose it work?

You should check the Selector structure.
From the apple docs:
In Swift, Objective-C selectors are represented by the Selector
structure. You can construct a selector with a string literal, such as
let mySelector: Selector = "tappedButton:". Because string literals
can be automatically converted to selectors, you can pass a string
literal to any method that accepts a selector.

Selector function with Swift :
func selectorFunc(aSel:Selector){
if self.respondsToSelector(aSel){
NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: aSel, userInfo: nil, repeats: false)
}
}
func gooleIt(){
println("Hello")
}
Function Call :
self.selectorFunc(Selector(gooleIt()))
Hope it help you.

You can declare a method with a selector like this and you can call it by creating a UIControl because performSelector methods are not available in Swift:
func methodWithSelector(sel:Selector) {
var control = UIControl()
control.sendAction(sel, to: self, forEvent: nil)
}
If you do not need to call it on the main thread another option is like this:
func methodWithSelector(sel:Selector) {
NSThread.detachNewThreadSelector(sel, toTarget: self, withObject: nil)
}
You will call it like this:
methodWithSelector(Selector("methodCall"))
or like this
methodWithSelector("methodCall")
then you must have a method with the name of the selector
func methodCall() {
println("methodCall")
}

Related

Passing param value to selector

I am declaring a selector and want to pass a label object as the parameter and not able to do so.
Below is my code:
public func animateLabelText(label: UILabel) {
caDisplayLink = CADisplayLink(target: self, selector: #selector(changeLabelText(label: label)))
}
#objc private func changeLabelText(label: UILabel) {
// Do something
}
I am seeing this error - Argument of '#selector' does not refer to an '#objc' method, property, or initializer.
How do I pass the parameter to selector?
The signature should be as following
public func animateLabelText(label: UILabel) {
caDisplayLink = CADisplayLink(target: self,
selector: #selector(changeLabelText(_:)))
}
#objc private func changeLabelText(_ label: UILabel) {
// Do something
}

Swift: Passing a parameter to selector

Using Swift 3, Xcode 8.2.1
Method:
func moveToNextTextField(tag: Int) {
print(tag)
}
The lines below compile fine, but tag has an uninitialized value:
let selector = #selector(moveToNextTextField)
Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: selector, userInfo: nil, repeats: false)
However, I need to pass a parameter. Below fails to compile:
let selector = #selector(moveToNextTextField(tag: 2))
Swift Compile Error:
Argument of #selector does not refer to an #objc method, property, or initializer.
How can I pass an argument to a selector?
#selector describes method signature only. In your case the correct way to initialize the selector is
let selector = #selector(moveToNextTextField(tag:))
Timer has the common target-action mechanism. Target is usually self and action is a method that takes one parameter sender: Timer. You should save additional data to userInfo dictionary, and extract it from sender parameter in the method:
func moveToNextTextField(sender: Timer) {
print(sender.userInfo?["tag"])
}
...
let selector = #selector(moveToNextTextField(sender:))
Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: selector, userInfo: ["tag": 2], repeats: false)
You cannot pass a custom parameter through a Timer action.
Either
#selector(moveToNextTextField)
...
func moveToNextTextField()
or
#selector(moveToNextTextField(_:))
...
func moveToNextTextField(_ timer : Timer)
is supported, nothing else.
To pass custom parameters use the userInfo dictionary.

NSTimer scheduledTimerWithTimeInterval - not calling funciton

I want to run a timer in the background. So I created a singleton.
The problem is that after the set 5.0 seconds, it does not call the function timeEnded(). Xcode proposes to add #Objc in front of the function (like this: #Objc func timeEnded() {...) to solve some problem (I don't get what, though). But it still doesn't call that function. Any ideas?
class TimerService {
static let instance = TimerService()
var internalTimer: NSTimer?
func startTimer() {
guard internalTimer != nil else {
return print("timer already started")
}
internalTimer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: #selector(TimerService.timeEnded), userInfo: nil, repeats: false)
}
func timeEnded() {
//NSNotificationCenter.defaultCenter().postNotificationName("timerEnded", object: nil)
print("timer Ended")
}
}
You never actually start the timer because your startTimer() function will always return before reaching the line of code where you create the timer.
In your guard statement you only continue the execution of the function if internalTimer != nil but the only place where you set the timer is after that statement. Thus, your timer is never created and internalTimer will always be nil.
This should fix your problem:
func startTimer() {
guard internalTimer == nil else {
return print("timer already started")
}
internalTimer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: #selector(TimerService.timeEnded), userInfo: nil, repeats: false)
}
Selectors are a feature of Objective-C and can only be used with methods that are exposed to the dynamic Obj-C runtime. You cannot have a selector to a pure Swift method.
If your class inherits from NSObject then its public methods are exposed to Obj-C automatically. Since your class does not inherit from NSObject you have to use the #objc attribute to indicate that you want this method exposed to Obj-C so that it may be called with an Obj-C selector.
#selector() is the new syntax in Swift 2.2. It allows the compiler to check that the selector you're trying to use actually exists. The old syntax is deprecated and will be removed in Swift 3.0.

Swift selector to protocol function?

I have code like this:
protocol FooP {
...
}
extension FooP {
func doFoo() {
print("foo")
}
func doFoo(timer: NSTimer) {
doFoo()
}
}
class A : NSObject, UITableViewDataSource, FooP {
var timer : NSTimer?
...
func startUpdating() {
timer = NSTimer.scheduledTimerWithTimeInterval(
1.0,
target: self,
selector: Selector("doFoo:"),
userInfo: nil,
repeats: true
)
}
}
Unfortunately it crashes when I start timer the program crashes with
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[xyz.A doFoo:]: unrecognized selector sent to instance 0x7fb2041c4ac0'
How i can make it work (I want to keep implementation of doFoo inside protocol)?
If I move doFoo into A class definition everything works fine, but as i said i want to implement this function inside protocol.
In other words I need selector that says
"Hey I point to function named "doFoo" that is implemented as extension to FooP"
Right now selector seems to say
"Hey I point to function named "doFoo" that is implemented in A class"
Try to play in your playground. Your trouble is, that there is no possibility to define #objc func in protocol extension. So, see possible workaround
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
import Foundation
protocol FooP {
}
extension FooP {
func doFoo() {
print("foo")
}
func doFoo(timer: NSTimer) {
print("dofoo")
doFoo()
}
}
class A: FooP {
var timer : NSTimer?
#objc func foo(timer: NSTimer) {
doFoo(timer)
}
func startUpdating() {
timer = NSTimer.scheduledTimerWithTimeInterval(
1.0,
target: self,
selector: "foo:",
userInfo: nil,
repeats: true
)
}
}
let a = A()
a.startUpdating()
Why it works for you if you move doFoo inside class A? That is because your class inherits from NSObject, so #objc keyword is not necessary.
The problem is, NSTimer and the whole Selector() business are Objective-C stuff and do work in Swift domain thanks to bridging. However, Swift's default protocol implementations are not bridged to Objective-C wonderland (yet), and that's why your timer fails. Basically, from Objective-C perspective objects of type A do not respond to a selector doFoo:, period.
So, report this use-case to swift-evolution for the long-term solution. Short-term, use some sort of a workaround.
BTW, you might find it interesting to read (or even participate) in this thread.

How to implement callback/selector with performSelector in swift?

I am trying to create an equivalent of below method signature (Objective-C) in swift language. I couldn't get an answer on how to get the right equivalent for this. Any help is highly appreciated.
- (void)myMethod:(MyObject*)firstParam
setCallbackObject:(id)obj
withMySelector:(SEL)selector {
[obj performSelector:selector withObject:nil afterDelay:0]
}
First:
NOTE
The performSelector: method and related selector-invoking methods are not imported in Swift because they are inherently unsafe.
If you still want to implement it that way, read below.
You could use NSTimer:
var myTimer: NSTimer = NSTimer.scheduledTimerWithTimeInterval(0.0, target: self, selector: "selectorMethod", userInfo: nil, repeats: false)
String can be used where Selector is needed. It will automatically be converted (autoboxing).
The delay can be of course higher: 0.1 is then equal to 1 tenth of a second.
To call a method like:
func selectorMethod() {
...
}
We need to check before using the selector on the class. But the respondsToSelector: is in the NSObject protocol, so you have to derive at least from that (or one that subclasses from it).
To make it clear, here is the example.
Code:
class Test {
func myMethod(firstParam: String, setCallbackObject obj: AnyObject, withMySelector selector: Selector) {
if obj.respondsToSelector(selector) {
var myTimer: NSTimer = NSTimer.scheduledTimerWithTimeInterval(0.0, target: obj, selector: selector, userInfo: nil, repeats: false)
myTimer.fire()
} else {
println("Warning: does not respond to given selector")
}
}
}
class Test2: NSObject {
func selectorMethod() {
print("worked")
}
}
var test: Test = Test()
var callBackObj: Test2 = Test2()
test.myMethod("thisfirstis", setCallbackObject: callBackObj, withMySelector: Selector("selectorMethod"))
Output:
workedProgram ended with exit code: 0

Resources