Delaying function in swift [duplicate] - ios

This question already has answers here:
dispatch_after - GCD in Swift?
(26 answers)
Closed 6 years ago.
I don't have a code to sample or anything, because I have no idea how to do it, but can someone please tell me how to delay a function with swift for a set amount of time?

You can use GCD (in the example with a 10 second delay):
Swift 2
let triggerTime = (Int64(NSEC_PER_SEC) * 10)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, triggerTime), dispatch_get_main_queue(), { () -> Void in
self.functionToCall()
})
Swift 3 and Swift 4
DispatchQueue.main.asyncAfter(deadline: .now() + 10.0, execute: {
self.functionToCall()
})
Swift 5 or Later
DispatchQueue.main.asyncAfter(deadline: .now() + 10.0) {
//call any function
}

Swift 3 and Above Version(s) for a delay of 10 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 10) { [unowned self] in
self.functionToCall()
}

NSTimer.scheduledTimerWithTimeInterval(NSTimeInterval(3), target: self, selector: "functionHere", userInfo: nil, repeats: false)
This would call the function functionHere() with a 3 seconds delay

For adding argument to delay function.
First setup a dictionary then add it as the userInfo. Unwrap the info with the timer as the argument.
let arg : Int = 42
let infoDict : [String : AnyObject] = ["argumentInt", arg]
NSTimer.scheduledTimerWithTimeInterval(NSTimeInterval(3), target: self, selector: "functionHereWithArgument:", userInfo: infoDict, repeats: false)
Then in the called function
func functionHereWithArgument (timer : NSTimer)
{
if let userInfo = timer.userInfo as? Dictionary<String, AnyObject>
{
let argumentInt : Int = (userInfo[argumentInt] as! Int)
}
}

Related

Swift - Background Fetch and Delay

I implemented Background Fetch feature in my App.
The function called by background fetch includes a delayed function.
However this function doesn't seem to trigger :
let when = DispatchTime.now() + 2
DispatchQueue.main.asyncAfter(deadline: when) {
MyFunction()
}
It seems to trigger however as soon as I wake up the app.
Why this doesn't trigger on background fetch ?
Try this code:
DispatchQueue.global().async {
let timer = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(self.MyFunction), userInfo: nil, repeats: false)
RunLoop.current.add(timer, forMode: .defaultRunLoopMode)
RunLoop.current.run()
}
I don't think you can run in the background on the main queue. Try this (changed main to global()):
let when = DispatchTime.now() + 2
DispatchQueue.global().asyncAfter(deadline: when) {
MyFunction()
}

How to program a delay in Swift 3

In earlier versions of Swift, one could create a delay with the following code:
let time = dispatch_time(dispatch_time_t(DISPATCH_TIME_NOW), 4 * Int64(NSEC_PER_SEC))
dispatch_after(time, dispatch_get_main_queue()) {
//put your code which should be executed with a delay here
}
But now, in Swift 3, Xcode automatically changes 6 different things but then the following error appears: "Cannot convert DispatchTime.now to expected value dispatch_time_t aka UInt64."
How can one create a delay before running a sequence of code in Swift 3?
After a lot of research, I finally figured this one out.
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { // Change `2.0` to the desired number of seconds.
// Code you want to be delayed
}
This creates the desired "wait" effect in Swift 3 and Swift 4.
Inspired by a part of this answer.
I like one-line notation for GCD, it's more elegant:
DispatchQueue.main.asyncAfter(deadline: .now() + 42.0) {
// do stuff 42 seconds later
}
Also, in iOS 10 we have new Timer methods, e.g. block initializer:
(so delayed action may be canceled)
let timer = Timer.scheduledTimer(withTimeInterval: 42.0, repeats: false) { (timer) in
// do stuff 42 seconds later
}
Btw, keep in mind: by default, timer is added to the default run loop mode. It means timer may be frozen when the user is interacting with the UI of your app (for example, when scrolling a UIScrollView)
You can solve this issue by adding the timer to the specific run loop mode:
RunLoop.current.add(timer, forMode: .common)
At this blog post you can find more details.
Try the following function implemented in Swift 3.0 and above
func delayWithSeconds(_ seconds: Double, completion: #escaping () -> ()) {
DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
completion()
}
}
Usage
delayWithSeconds(1) {
//Do something
}
Try the below code for delay
//MARK: First Way
func delayForWork() {
delay(3.0) {
print("delay for 3.0 second")
}
}
delayForWork()
// MARK: Second Way
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
// your code here delayed by 0.5 seconds
}
One way is to use DispatchQueue.main.asyncAfter as a lot of people have answered.
Another way is to use perform(_:with:afterDelay:). More details here
perform(#selector(delayedFunc), with: nil, afterDelay: 3)
#IBAction func delayedFunc() {
// implement code
}
Most common things to use are asyncAfter() and Timer. But if blocking thread is OK, then there is an option:
sleep(3) // in seconds
usleep // in 1/million of second
For asynchronous programming (Swift 5.5) pausing in func looks like this:
func someAsyncFunc() async {
await Task.sleep(2_000_000_000) // Two seconds
// Code to be executed with a delay here
}
//Runs function after x seconds
public static func runThisAfterDelay(seconds: Double, after: #escaping () -> Void) {
runThisAfterDelay(seconds: seconds, queue: DispatchQueue.main, after: after)
}
public static func runThisAfterDelay(seconds: Double, queue: DispatchQueue, after: #escaping () -> Void) {
let time = DispatchTime.now() + Double(Int64(seconds * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
queue.asyncAfter(deadline: time, execute: after)
}
//Use:-
runThisAfterDelay(seconds: x){
//write your code here
}

call function periodically at random intervals in Swift

I am trying to call a function 10 times at random intervals between them.
How can I achieve this?
I did come up with a method, but is is horribly ugly. It looks like this:
var counter = 0
NSTimer.scheduledTimerWithTimeInterval(arc4random_uniform(4)+2, target: self, selector: Selector("createNewTimer"), userInfo: nil, repeats: false)
func createNewTimer(){
// PERFORM STUFF YOU NEED TO
counter++
if counter <= 10{
NSTimer.scheduledTimerWithTimeInterval(arc4random_uniform(4)+2, target: self, selector: Selector("createNewTimer"), userInfo: nil, repeats: false)
}
}
Is there a nicer way of calling function at random intervals?
Try this in the playground. Hope it helps:
func after(delay: Double, block: () -> Void) {
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
block()
}
}
func repeatBlock(counter: Int = 1, times: Int, block: () -> Void) {
after(Double(arc4random_uniform(4) + 2)) {
block()
if counter < times {
repeatBlock(counter + 1, times: times, block: block)
}
}
}
//client's code
var counter = 0
repeatBlock(times: 10) {
//your code here
print(NSDate())
}
sleep(100)
If you need a random intervals, then I'm afraid, the above is the best solution. If the time interval was same, then you could have set it to repeats:true. But not in you case.

NSTimer - how to delay in Swift

I have a problem with delaying computer's move in a game.
I've found some solutions but they don't work in my case, e.g.
var delay = NSTimer.scheduledTimerWithTimeInterval(4, target: self, selector: nil, userInfo: nil, repeats: false)
I tried to use this with function fire but also to no effects.
What other possibilities there are?
Swift 3
With GCD:
let delayInSeconds = 4.0
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delayInSeconds) {
// here code perfomed with delay
}
or with a timer:
func myPerformeCode() {
// here code to perform
}
let myTimer : Timer = Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(self.myPerformeCode), userInfo: nil, repeats: false)
Swift 2
With GCD:
let seconds = 4.0
let delay = seconds * Double(NSEC_PER_SEC) // nanoseconds per seconds
let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(dispatchTime, dispatch_get_main_queue(), {
// here code perfomed with delay
})
or with a timer:
func myPerformeCode(timer : NSTimer) {
// here code to perform
}
let myTimer : NSTimer = NSTimer.scheduledTimerWithTimeInterval(4, target: self, selector: Selector("myPerformeCode:"), userInfo: nil, repeats: false)
With Swift 4.2
With Timer You can avoid using a selector, using a closure instead:
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { (nil) in
// Your code here
}
Keep in mind that Timer is toll-free bridged with CFRunLoopTimer, and that run loops and GCD are two completely different approaches.... e
In swift we can delay by using Dispatch_after.
SWift 3.0 :-
DispatchQueue.main.asyncAfter(deadline: .now()+4.0) {
alert.dismiss(animated: true, completion: nil)
}
How about using Grand Central Dispatch?
https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/index.html
Valfer has shown you how

Swift performSelector:withObject:afterDelay: is unavailable [duplicate]

This question already has answers here:
dispatch_after - GCD in Swift?
(26 answers)
Closed 8 years ago.
I have an app in Objective C that I'm transitioning to Swift. In Objective C, I have this method:
[self.view performSelector:#selector(someSelector) withObject:self afterDelay:0.1f];
I'm working with Swift and I can't figure out how to do this. I've tried:
self.view.performSelector(Selector("someSelector"), withObject: self, afterDelay: 0.1)
Here's the error that I get: 'performSelector' is unavailable: 'performSelector' methods are unavailable
What call would I use to call a method afterDelay?
UPDATE
Here's what I ended up with:
extension NSObject {
func callSelectorAsync(selector: Selector, object: AnyObject?, delay: NSTimeInterval) -> NSTimer {
let timer = NSTimer.scheduledTimerWithTimeInterval(delay, target: self, selector: selector, userInfo: object, repeats: false)
return timer
}
func callSelector(selector: Selector, object: AnyObject?, delay: NSTimeInterval) {
let delay = delay * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue(), {
NSThread.detachNewThreadSelector(selector, toTarget:self, withObject: object)
})
}
}
Swift 4
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
// your function here
}
Swift 3
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(0.1)) {
// your function here
}
Swift 2
let dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC)))
dispatch_after(dispatchTime, dispatch_get_main_queue(), {
// your function here
})
You could do this:
var timer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector("someSelector"), userInfo: nil, repeats: false)
func someSelector() {
// Something after a delay
}
SWIFT 3
let timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(someSelector), userInfo: nil, repeats: false)
func someSelector() {
// Something after a delay
}
Swift is statically typed so the performSelector: methods are to fall by the wayside.
Instead, use GCD to dispatch a suitable block to the relevant queue — in this case it'll presumably be the main queue since it looks like you're doing UIKit work.
EDIT: the relevant performSelector: is also notably missing from the Swift version of the NSRunLoop documentation ("1 Objective-C symbol hidden") so you can't jump straight in with that. With that and its absence from the Swiftified NSObject I'd argue it's pretty clear what Apple is thinking here.

Resources