15

apparently the following is not available in swift anymore:

[self performSelector:@selector(onFlip) withObject:nil afterDelay:0.3];

why is this the case if the following is still present:

NSObject.cancelPreviousPerformRequestsWithTarget(self, selector: singleTapSelector, object: nil)

doesn't nsobject.cancel work with performSelector? why have the cancel functionality but not the perform functionality?

Abdul Ahmad
  • 9,673
  • 16
  • 64
  • 127
  • 1
    What do you mean exactly by _not cancelling_? This is supposed to cancel any prior calls to `performSelector:withObject:afterDelay:` for this selector. Do you have such a call? If yes, could you include it in the question? – Alladinian Dec 21 '14 at 20:15
  • @Alladinian yes I do, I'll add it, please see edits – Abdul Ahmad Dec 21 '14 at 20:15
  • @Alladinian does the edit make more sense now? – Abdul Ahmad Dec 21 '14 at 20:25
  • 1
    Your first line is incorrect. It needs to be `let singleTapSelector = Selector("singleTap")` – qwerty_so Dec 21 '14 at 20:27
  • @ThomasKilian thank you will fix that, will this fix my issue? – Abdul Ahmad Dec 21 '14 at 20:28
  • At least it would help to compile the cancel... statement which in the original version most likely wouldn't. – qwerty_so Dec 21 '14 at 20:32
  • I checked the NSObject documentation and according to that the method is available. Though code completion does not show it. Strange. – qwerty_so Dec 21 '14 at 21:22
  • 1
    I inserted `performSelector(Selector("a"), withObject:nil, afterDelay:1.0)` and the compiler fails with "not available". But the alt-click shows the help for it telling "available in 10.10". Smells like a bug (there are plenty already). – qwerty_so Dec 21 '14 at 21:26

5 Answers5

16

Use

NSTimer.scheduledTimerWithTimeInterval(delay, target: self, selector: "onFlip", userInfo: nil, repeats: false)

instead. No idea why the other one is not available.

Jack Humphries
  • 13,056
  • 14
  • 84
  • 125
qwerty_so
  • 35,448
  • 8
  • 62
  • 86
  • thanks for looking into it, I'll try this and get back to you! – Abdul Ahmad Dec 21 '14 at 21:32
  • by the way, can I use the cancelPrevious... with this NSTimer call? – Abdul Ahmad Dec 21 '14 at 21:35
  • 1
    just did this, and to cancel the timer I used timer.invalidate(), thanks for the help! – Abdul Ahmad Dec 21 '14 at 21:39
  • @Thomas Kilian, Method in Objective-C [didBeginEditingTarget performSelector:didBeginEditingSelector]; Instead of Above Method in Swift NSTimer.scheduledTimerWithTimeInterval(0, target: didBeginEditingTarget, selector: didBeginEditingSelector, userInfo: nil, repeats: false) Both are a WORKING SAME? Please let us know your comments. – Renish Dadhaniya Jun 05 '15 at 12:45
  • @RenishDadhaniya You should ask that as new question – qwerty_so Jun 05 '15 at 12:51
  • Sorry for inconvenience, I think it is subject to related and you simply put your comments here. It's helpful for another person.Thank you so much to give replay..I already UP your Answer(+1). – Renish Dadhaniya Jun 05 '15 at 12:54
11

We can easily unlock these methods in Swift 1.x! Yay!

These methods from the perfromSelector family actually exist in Swift but they were artificially hidden (by design, I think) prior Swift 2.0. They can be unveiled using swizzling and some Objective-C runtime hacks right in Swift.

Just add this extension file NSObject+PerformSelector.swift and prefix all calls with symbol.

In your case the code:

[self performSelector:@selector(onFlip) withObject:nil afterDelay:0.3];

can be rewritten in Swift like this:

self.performSelector("onFlip", withObject: nil, afterDelay: 0.3)

With using this magical category (class extension): NSObject+PerformSelector.swift

import Foundation

private var dispatchOnceToken: dispatch_once_t = 0

private var selectors: [Selector] = [
    "performSelector:",
    "performSelector:withObject:",
    "performSelector:withObject:withObject:",
    "performSelector:withObject:afterDelay:inModes:",
    "performSelector:withObject:afterDelay:",
]

private func swizzle() {
    dispatch_once(&dispatchOnceToken) {
        for selector: Selector in selectors {
            let selector = Selector("\(selector)")
            let method = class_getInstanceMethod(NSObject.self, selector)

            class_replaceMethod(
                NSObject.self,
                selector,
                method_getImplementation(method),
                method_getTypeEncoding(method)
            )
        }
    }
}

extension NSObject {

    func performSelector(selector: Selector) -> AnyObject? {
        swizzle()
        return self.performSelector(selector)
    }

    func performSelector(selector: Selector, withObject object: AnyObject?) -> AnyObject? {
        swizzle()
        return self.performSelector(selector, withObject: object)
    }

    func performSelector(selector: Selector, withObject object1: AnyObject?, withObject object2: AnyObject?) -> AnyObject? {
        swizzle()
        return self.performSelector(selector, withObject: object1, withObject: object2)
    }

    func performSelector(selector: Selector, withObject object: AnyObject?, afterDelay delay: NSTimeInterval, inModes modes: [AnyObject?]?) {
        swizzle()
        self.performSelector(selector, withObject: object, afterDelay: delay, inModes: modes)
    }

    func performSelector(selector: Selector, withObject object: AnyObject?, afterDelay delay: NSTimeInterval) {
        swizzle()
        self.performSelector(selector, withObject: object, afterDelay: delay)
    }

}
Valentin Shergin
  • 7,166
  • 2
  • 50
  • 53
  • 3
    Why the rocket symbol? – saagarjha Aug 30 '15 at 02:27
  • 1
    Because Emoji is fun and we can use it in Swift! My first intension was `` banana symbol but I decided to switch to more conformist-like `` rocket symbol. Of course any another prefix can be used. – Valentin Shergin Aug 30 '15 at 02:48
  • Yes, but why is a prefix even needed? – saagarjha Aug 30 '15 at 20:23
  • 1
    We have to use a prefix because otherwise Xcode reports an error: `Method 'performSelector' with Objective-C selector 'performSelector:' conflicts with previous declaration with the same Objective-C selector`. – Valentin Shergin Aug 31 '15 at 05:18
  • 2
    Honestly, I think that allowing emoji was a most silly idea. So instead of typing via keyboard I have to click around in annoying tables. This isn't funny at all. The best would be to say it's silly. – qwerty_so Mar 24 '16 at 19:44
  • From Swift prospective, emojis is not a point at all, the point is that you can use any character from any language whatever you want. Actually, I may have hardware emoji keyboard as somebody may have greek keyboard. – Valentin Shergin Mar 24 '16 at 19:50
  • Imagine a day, when the code is full of emojis, just emojis! – asyncwait Jun 17 '16 at 09:24
5
func performFlipOnDelay() {
  let delayInSeconds = 1.0
  let delay = dispatch_time(DISPATCH_TIME_NOW, Int64(delayInSeconds * Double(NSEC_PER_SEC)))
  dispatch_after(delay, dispatch_get_main_queue()) { 
     onFlip();
  }
}

Another round about answer would be the above using GCD to add a delay time for an action to be performed

TheCodingArt
  • 3,436
  • 4
  • 30
  • 53
3

Taking the answer from Thomas Kilian a step further, primarily to enhance readability on calling clients, I've defined a swift utility, TimerUtil.swift, defining a facade on NSTimer for this particular usecase:

import Foundation

/**
 * Convenience function to make a single timer scheduling more readable.
 */
public func invoke( selector:Selector, on target:AnyObject, afterDelay delay:NSTimeInterval ) {

    NSTimer.scheduledTimerWithTimeInterval( delay, target: target, selector: selector, userInfo: nil, repeats: false )
}

Then your code can simply call:

invoke( "onFlip", on:self, afterDelay:0.3 )

which more or less resembles performSelector:withObject:afterDelay

buzo
  • 81
  • 3
1

In Swift 4 use This to Handle These Types of Warning!

perform(#selector(yourMethodName), with: nil, afterDelay: 0, inModes: [])
elp
  • 8,021
  • 7
  • 61
  • 120
Rishabh Shukla
  • 461
  • 6
  • 19