390

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?

Karthik Kumar
  • 1,285
  • 1
  • 11
  • 29
owlswipe
  • 18,546
  • 9
  • 36
  • 80

7 Answers7

1120

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.

Daniel Storm
  • 17,279
  • 7
  • 80
  • 145
owlswipe
  • 18,546
  • 9
  • 36
  • 80
  • 8
    Useful contribution, thanks! Updating for the most recent Swift 3: `DispatchQueue.main.asyncAfter(deadline: when)` – Rogare Sep 07 '16 at 16:58
  • 85
    You could make your code a bit more swifty by replacing "+ 2" with "+ .seconds(2)". Or, for the ultimate in swiftyness, you could drop the first line and replace "deadline: when" with "deadline: .now() + .seconds(2)". – RenniePet Jan 03 '17 at 07:12
  • 2
    @OctavioAntonioCedeño Happy to help. This really bugged me for a while :D – owlswipe Mar 10 '17 at 00:10
  • 5
    Still working 3/12/2017. Thank you very much for this :) – John Leonardo Mar 13 '17 at 02:54
  • 1
    @JohnLeonardo You're welcome :D. I'll update it if it ever stops working! – owlswipe Mar 13 '17 at 04:26
  • 1
    if the delay is long, say 10 min, and the phone goes to sleep. the task will never be run even after the phone is woken up after 10 min. – jiawen Mar 16 '17 at 16:12
  • 1
    @jiawen That's true, but that would be the case with any such delay. I can't think of a use case other than notifications where this could be an issue, and for waiting to send a notification you should definitely use `.firedate` instead. – owlswipe Mar 16 '17 at 20:47
  • 1
    Working (27th March 2017) – Naveed Ahmad May 26 '17 at 21:26
  • 1
    Yes, I'll keep it working once Swift 4 rolls around since this is by far my most upvoted post :)). Also, friendly heads up: it's may not march! – owlswipe May 26 '17 at 23:40
  • Haha just came back to this post because I needed it again. Thanks bro. – John Leonardo Jun 18 '17 at 00:26
  • @JohnLeonardo Thanks :)) I feel so appreciated! – owlswipe Jun 18 '17 at 14:36
  • but this function available iOS (10.0 and later) – Ammaiappan Jun 26 '17 at 10:02
  • @Ammaiappan Yep, it's working on iOS 10 + Swift 3. I'll check about iOS 11 and Swift 4 when they come out as final releases. – owlswipe Jun 26 '17 at 13:16
  • 1
    Working on iOS 11 and Swift 4! – owlswipe Oct 27 '17 at 23:11
  • I used this code but pigs started flying and I got scared so I deleted it. I guess you have to be extra careful with asynchronous calls. – Nerdy Bunz Feb 07 '18 at 12:39
  • I've got a problem with this answer, I wanted to trigger a code every second or less and it came out that it's not called exactly one second and there are discrepancies: https://stackoverflow.com/questions/49347450/discrepancies-in-time-when-using-dispatchqueue-in-swift – mikro098 Mar 18 '18 at 11:48
  • @codddeer123 I suspect what's happening asynchronously may not be happening on time! The answer in your question there should explain more in detail. – owlswipe Mar 18 '18 at 16:45
  • 3
    "After a lot of research, I finally figured this one out"... the question and the answer has the same datetime lol – pableiros May 14 '19 at 22:40
  • 1
    @pableiros After I figured it out I made the question and answer to help others looking for the same thing :) – owlswipe Jun 07 '19 at 15:40
  • 7
    Literally my most visited post on SO. Its easier to find this post than actually remember it or find it elsewhere in my code ;) – barrylachapelle Nov 07 '19 at 19:40
202

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.

byaruhaf
  • 3,485
  • 2
  • 28
  • 47
Victor Do
  • 2,107
  • 1
  • 12
  • 9
60

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
}
Vakas
  • 5,890
  • 3
  • 36
  • 47
18

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
}
Peter
  • 25
  • 1
  • 5
Anand Verma
  • 522
  • 6
  • 11
  • 1
    First way displays have error "Use of unresolved identifier 'delay'" – Jerry Chong Aug 06 '19 at 02:56
  • 15
    This programmer is working with a helper method in his code base and has for a long time. So delay was code he has used for a while not knowing it is not apart of Apple's SDK. – Nick Perkins Oct 17 '19 at 20:21
4

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
}
Zohaib Brohi
  • 41
  • 1
  • 3
3

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
}
Paul B
  • 2,408
  • 21
  • 33
1

//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
}
Pratyush Pratik
  • 653
  • 7
  • 14