I want to schedule a function call in the future. I'm using Swift.
I want to callback a method that is private and returns a Promise (from PromiseKit)
All the example I've seen use
NSTimer.scheduledTimerWithTimeInterval(ti: NSTimeInterval, target: AnyObject, selector: Selector, userInfo: AnyObject?, repeats: Bool)
Fine. I've tried
NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "connect", userInfo: nil, repeats: false)
That fails with No method declared with Objective-C selector 'connect'
.
What is Objective-C doing here??
Anyway it's suggested that I add @objc
in front of my method connect
. Fine. Well I can't because apparently Method cannot be marked @objc because its result type cannot be represented in Objective-C
If I wanted to use objective-C I'd not be writing Swift...
There's another scheduledTimerWithTimeInterval
that is
NSTimer.scheduledTimerWithTimeInterval(ti: NSTimeInterval, invocation: NSInvocation, repeats: Bool)
But from what I've read NSInvocation
is not a Swift thing...
So I ended up creating a wrapper that does nothing other than calling connect
and returning Void
that Objective C can understand. It works but it feels very stupid. Is there a better Swift way?
Bonus: why can javascript do that as simply as setTimeout(this.connect, 1)
and Swift has no built in way I can find?
Beginning with iOS 10 and Swift 3, it is possible to use (NS)Timer with a block closure and thus avoid Objective-C selector invocation when the timer fires:
if #available(iOS 10.0, *) {
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false, block: { (Timer) in
self.connect() // per the OP's example
})
}
In addition to avoiding the @objc
decorator, using this technique allows you to invoke methods that include non-Objective-C-compatible parameter types such as enums and optionals.
Re: setTimeout(this.connect, 1)
from the Javascript, if you don't need to cancel it, a more direct analogy in Swift 3 might be:
DispatchQueue.Main.asyncAfter(deadline: .now() + 1.0, execute { self.connect() })
Which is pretty darn close given that you actually have a choice of which thread to run on ;-)
What is Objective-C doing here??
The reason for the need of Objective-C is that it dynamically binds the "call" (it is no call in Objective-C) at runtime, while Swift cannot do this. Swift is not able to have code in the timer class that "calls" a "function" not known at the compile time of NSTimer
.
BTW: NSTimer
uses NSInvocation
(or something similar unswifty technology) to do the the "call". Therefore the usage of NSTimer
is not more swifty, but the need for late binding is more obfuscated to make Swift developers feel better.
If I wanted to use objective-C I'd not be writing Swift...
Even your code is completely written in Swift, it takes masses of benefits from Objective-C's late binding. Many of the core techniques of Cocoa are impossible to write in Swift, including responder chain, undo manager, Core Data, animations … (On the other hand you are able to define a 💩 operator, what is a big progress in software engineering and describes the whole story.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With