Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift - scheduledTimerWithTimeInterval - NSInvocation

Tags:

swift

timer

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?

like image 884
Guig Avatar asked Jul 05 '16 02:07

Guig


2 Answers

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 ;-)

like image 197
mygzi Avatar answered Nov 09 '22 05:11

mygzi


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.)

like image 1
Amin Negm-Awad Avatar answered Nov 09 '22 05:11

Amin Negm-Awad