How to use background thread in swift? [closed]
Asked Answered
S

18

424

How to use threading in swift?

dispatchOnMainThread:^{

    NSLog(@"Block Executed On %s", dispatch_queue_get_label(dispatch_get_current_queue()));

}];
Spikes answered 5/6, 2014 at 9:16 Comment(4)
Which part do you have trouble converting?Conchitaconchobar
Why do you have ] before the semicolon in the last line?Legendre
it would be helpful ifyou explain where you are stuck or which you need help with.Octennial
DispatchQueue.global(qos: .background).async { print("Run on background thread") DispatchQueue.main.async { print("We finished that.") // only back on the main thread, may you access UI: label.text = "Done." } }Propose
E
847

Swift 3.0+

A lot has been modernized in Swift 3.0. Running something on a background queue looks like this:

DispatchQueue.global(qos: .userInitiated).async {
    print("This is run on a background queue")

    DispatchQueue.main.async {
        print("This is run on the main queue, after the previous code in outer block")
    }
}

Swift 1.2 through 2.3

let qualityOfServiceClass = QOS_CLASS_USER_INITIATED
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
    print("This is run on a background queue")

    dispatch_async(dispatch_get_main_queue(), { () -> Void in
        print("This is run on the main queue, after the previous code in outer block")
    })
})

Pre Swift 1.2 – Known issue

As of Swift 1.1 Apple didn't support the above syntax without some modifications. Passing QOS_CLASS_USER_INITIATED didn't actually work, instead use Int(QOS_CLASS_USER_INITIATED.value).

For more information see Apples documentation

Ethelethelbert answered 31/7, 2014 at 23:15 Comment(8)
And if someone wants a more Swift like syntax, I've created Async that adds some sugar to the syntax like Async.background {}Ethelethelbert
I am using your code in xCode 6.0.1 and ios 8.It gives error as "QOS_CLASS_BACKGROUND" return class and it is of type UInt32 and "dispatch_get_global_queue" requires 1st parameter as int so type error is coming.Coldhearted
So in Xcode 6.1.1 I am not getting an error for using just plain simple "QOS_CLASS_BACKGROUND". Is it fixed?Ventriloquy
@LucasGoossen Yes, it has been fixed. I've update the post accordingly.Ethelethelbert
iOS 7 doesn't support this feature. So avoid it if you want older OS supportDevour
@NikitaPronchik Isn't this clear from the answer? Else feel free to make a edit to it.Ethelethelbert
Beware that you should only be using the .background QoS class for tasks that have low priority. Often .userInitiated is what you are really after.Wayward
Very much agreed @BalázsVincze, thanks! I’ll update with .userInitiated since seems like a more sensible default.Ethelethelbert
A
203

Dan Beaulieu's answer in swift5 (also working since swift 3.0.1).

Swift 5.0.1

extension DispatchQueue {

    static func background(delay: Double = 0.0, background: (()->Void)? = nil, completion: (() -> Void)? = nil) {
        DispatchQueue.global(qos: .background).async {
            background?()
            if let completion = completion {
                DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: {
                    completion()
                })
            }
        }
    }

}

Usage

DispatchQueue.background(delay: 3.0, background: {
    // do something in background
}, completion: {
    // when background job finishes, wait 3 seconds and do something in main thread
})

DispatchQueue.background(background: {
    // do something in background
}, completion:{
    // when background job finished, do something in main thread
})

DispatchQueue.background(delay: 3.0, completion:{
    // do something in main thread after 3 seconds
})
Activate answered 6/12, 2016 at 14:25 Comment(7)
Amazing, thank you for updating so nicely to the Swift 3.0.1 format!Lunn
I use extensions more than any living person. But there's a real danger in using an extension that is no different, at all, from the original!Overtone
@Frouo Very elegant, is it possible to add a completion handler to when 4 async calls all finish? I know its a bit off-topic.Carmelo
@GitSyncApp - possibly look in to DispatchGroups for what you want. Many QA on here about it.Overtone
yup forget that link. all you need is a dispatch group - it's very very simple; no worries at all !Overtone
It helped me but one thing i want to ask that how to run this method for infinite time in backgroundMoonwort
@DilipJangid you can't, unless your job in the background closure is very very very long (~=infinite). This method is made to last for a finite time: the time your background job needs to execute. So, completion closure will be called as soon as your background job execution time + delay has passed.Activate
L
126

The best practice is to define a reusable function that can be accessed multiple times.

REUSABLE FUNCTION:

e.g. somewhere like AppDelegate.swift as a Global Function.

func backgroundThread(_ delay: Double = 0.0, background: (() -> Void)? = nil, completion: (() -> Void)? = nil) {
    dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.value), 0)) {
        background?()

        let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
        dispatch_after(popTime, dispatch_get_main_queue()) {
            completion?()
        }
    }
}

Note: in Swift 2.0, replace QOS_CLASS_USER_INITIATED.value above with QOS_CLASS_USER_INITIATED.rawValue instead

USAGE:

A. To run a process in the background with a delay of 3 seconds:

    backgroundThread(3.0, background: {
            // Your background function here
    })

B. To run a process in the background then run a completion in the foreground:

    backgroundThread(background: {
            // Your function here to run in the background
    },
    completion: {
            // A function to run in the foreground when the background thread is complete
    })

C. To delay by 3 seconds - note use of completion parameter without background parameter:

    backgroundThread(3.0, completion: {
            // Your delayed function here to be run in the foreground
    })
Lunn answered 15/6, 2015 at 9:13 Comment(9)
nice snippet, should be the correct answer. @Dale CliffordLitigant
Brilliant high level modern Swift-y approach to access old-timey GCD methods from the low level C library. Should come standard in Swift.Spake
Very nice. Would you please confirm, the delay only works for the completion block. So that means that the delay in A. has no impact, and the background block executes immediately without delay.Socialize
What happens, if you add a delay to option B aswell?Selig
You should be able to replace if(background != nil){ background!(); } with background?() for a somewhat swiftier syntax?Gallia
Could you please update this for Swift 3? The auto converter turned it into DispatchQueue.global(priority: Int(DispatchQoS.QoSClass.userInitiated.rawValue)).async { but this throws an error like cannot invoke initializer for type 'Int' with an argument list of type '(qos_class_t)'. A working solution is found here (DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async).Rhines
Also as a best practice, consider to put it in extension of UIViewControllerHartung
Wonderful! This should be the Right Answer for anyone planning on RE-USING this Background process stuff :-)Fourteenth
This won't work anymore with the newest version of swift I guess... Lots of errors when I try to add this.Criminality
G
58

In Swift 4.2 and Xcode 10.1

We have three types of Queues :

1. Main Queue: Main queue is a serial queue which is created by the system and associated with the application main thread.

2. Global Queue : Global queue is a concurrent queue which we can request with respect to the priority of the tasks.

3. Custom queues : can be created by the user. Custom concurrent queues always mapped into one of the global queues by specifying a Quality of Service property (QoS).

DispatchQueue.main//Main thread
DispatchQueue.global(qos: .userInitiated)// High Priority
DispatchQueue.global(qos: .userInteractive)//High Priority (Little Higher than userInitiated)
DispatchQueue.global(qos: .background)//Lowest Priority
DispatchQueue.global(qos: .default)//Normal Priority (after High but before Low)
DispatchQueue.global(qos: .utility)//Low Priority
DispatchQueue.global(qos: .unspecified)//Absence of Quality

These all Queues can be executed in two ways

1. Synchronous execution

2. Asynchronous execution

DispatchQueue.global(qos: .background).async {
    // do your job here
    DispatchQueue.main.async {
        // update ui here
    }
}

//Perform some task and update UI immediately.
DispatchQueue.global(qos: .userInitiated).async {  
    // Perform task
    DispatchQueue.main.async {  
        // Update UI
        self.tableView.reloadData()  
    }
}

//To call or execute function after some time
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
    //Here call your function
}

//If you want to do changes in UI use this
DispatchQueue.main.async(execute: {
    //Update UI
    self.tableView.reloadData()
})

From AppCoda : https://www.appcoda.com/grand-central-dispatch/

//This will print synchronously means, it will print 1-9 & 100-109
func simpleQueues() {
    let queue = DispatchQueue(label: "com.appcoda.myqueue")

    queue.sync {
        for i in 0..<10 {
            print("🔴", i)
        }
    }

    for i in 100..<110 {
        print("Ⓜ️", i)
    }
}

//This will print asynchronously 
func simpleQueues() {
    let queue = DispatchQueue(label: "com.appcoda.myqueue")

    queue.async {
        for i in 0..<10 {
            print("🔴", i)
        }
    }

    for i in 100..<110 {
        print("Ⓜ️", i)
    }
}
Gerardgerardo answered 16/8, 2018 at 12:51 Comment(4)
Best tutorial for threads medium.com/@gabriel_lewis/…Gerardgerardo
I didn't see any changes when you use .background QoS or .userInitiated but for me it worked out with .backgroundAverell
You may not see any difference between using .background and .userInitiated QoS because the system can override your setting and promote the .background QoS to .userInitiated. That's a behind-the-scenes optimization for queues used from within the main UI queue to make them match the QoS of the parent. You can check the QoS of the current thread using Thread.current.qualityOfService.Putnem
DispatchQueue.main.async does not at all do something on the UX thread immediately. It simply stacks it to be done, after, other items being done are doneOvertone
D
49

Swift 3 version

Swift 3 utilizes new DispatchQueue class to manage queues and threads. To run something on the background thread you would use:

let backgroundQueue = DispatchQueue(label: "com.app.queue", qos: .background)
backgroundQueue.async {
    print("Run on background thread")
}

Or if you want something in two lines of code:

DispatchQueue.global(qos: .background).async {
    print("Run on background thread")

    DispatchQueue.main.async {
        print("We finished that.")
        // only back on the main thread, may you access UI:
        label.text = "Done."
    }
}

You can also get some in-depth info about GDC in Swift 3 in this tutorial.

Discriminating answered 16/6, 2016 at 15:13 Comment(1)
Said. Since your answer is the best, I threw in a line of code showing how you "call back when finished". Feel free to unwind or edit, cheersOvertone
C
35

From Jameson Quave's tutorial

Swift 2

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
    //All stuff here
})
Cobden answered 5/6, 2014 at 9:23 Comment(5)
Just for clarification, why would this be used instead of the accepted answer? Is this just an older API?Somatotype
@Sirens I would think this would be very useful for apps supporting < iOS 8.Purr
I use this for iOs 8.2 to force processes.Alhambra
DISPATCH_QUEUE_PRIORITY_DEFAULT reverts to QOS_CLASS_DEFAULT. So I guess you could say it's more high level / accepted syntax.Huber
A DispatchQueue is not a thread.Damicke
W
29

Swift 5

To make it easy, create a file "DispatchQueue+Extensions.swift" with this content :

import Foundation

typealias Dispatch = DispatchQueue

extension Dispatch {

    static func background(_ task: @escaping () -> ()) {
        Dispatch.global(qos: .background).async {
            task()
        }
    }

    static func main(_ task: @escaping () -> ()) {
        Dispatch.main.async {
            task()
        }
    }
}

Usage :

Dispatch.background {
    // do stuff

    Dispatch.main { 
        // update UI
    }
}
Wellbeloved answered 1/4, 2020 at 12:46 Comment(0)
P
28

Swift 4.x

Put this in some file:

func background(work: @escaping () -> ()) {
    DispatchQueue.global(qos: .userInitiated).async {
        work()
    }
}

func main(work: @escaping () -> ()) {
    DispatchQueue.main.async {
        work()
    }
}

and then call it where you need:

background {
     //background job
     main {
       //update UI (or what you need to do in main thread)
     }
}
Pedicab answered 20/8, 2018 at 9:13 Comment(0)
C
22

You have to separate out the changes that you want to run in the background from the updates you want to run on the UI:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
    // do your task

    dispatch_async(dispatch_get_main_queue()) {
        // update some UI
    }
}
Crownpiece answered 7/7, 2015 at 8:38 Comment(2)
So dispatch_async(dispatch_get_main_queue()) { // update some UI } is called when the background statement(Outer Block) is done executing?Joslyn
Isn't this only for Swift 2.3 and below?Foxworth
F
9

Good answers though, anyway I want to share my Object Oriented solution Up to date for swift 5.

please check it out: AsyncTask

Conceptually inspired by android's AsyncTask, I've wrote my own class in Swift

AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread.

Here are few usage examples

Example 1 -

AsyncTask(backgroundTask: {(p:String)->Void in//set BGParam to String and BGResult to Void
        print(p);//print the value in background thread
    }).execute("Hello async");//execute with value 'Hello async'

Example 2 -

let task2=AsyncTask(beforeTask: {
           print("pre execution");//print 'pre execution' before backgroundTask
        },backgroundTask:{(p:Int)->String in//set BGParam to Int & BGResult to String
            if p>0{//check if execution value is bigger than zero
               return "positive"//pass String "poitive" to afterTask
            }
            return "negative";//otherwise pass String "negative"
        }, afterTask: {(p:String) in
            print(p);//print background task result
    });
    task2.execute(1);//execute with value 1

It has 2 generic types:

  • BGParam - the type of the parameter sent to the task upon execution.

  • BGResult - the type of the result of the background computation.

    When you create an AsyncTask you can those types to whatever you need to pass in and out of the background task, but if you don't need those types, you can mark it as unused with just setting it to: Void or with shorter syntax: ()

When an asynchronous task is executed, it goes through 3 steps:

  1. beforeTask:()->Void invoked on the UI thread just before the task is executed.
  2. backgroundTask: (param:BGParam)->BGResult invoked on the background thread immediately after
  3. afterTask:(param:BGResult)->Void invoked on the UI thread with result from the background task
Frascati answered 4/2, 2016 at 18:52 Comment(1)
This works wonderfully for me. Nice work, why not put it on github?Smithsonite
L
9

Since the OP question has already been answered above I just want to add some speed considerations:

I don't recommend running tasks with the .background thread priority especially on the iPhone X where the task seems to be allocated on the low power cores.

Here is some real data from a computationally intensive function that reads from an XML file (with buffering) and performs data interpolation:

Device name / .background / .utility / .default / .userInitiated / .userInteractive

  1. iPhone X: 18.7s / 6.3s / 1.8s / 1.8s / 1.8s
  2. iPhone 7: 4.6s / 3.1s / 3.0s / 2.8s / 2.6s
  3. iPhone 5s: 7.3s / 6.1s / 4.0s / 4.0s / 3.8s

Note that the data set is not the same for all devices. It's the biggest on the iPhone X and the smallest on the iPhone 5s.

League answered 16/5, 2018 at 12:37 Comment(0)
M
6

Multi purpose function for thread

public enum QueueType {
        case Main
        case Background
        case LowPriority
        case HighPriority

        var queue: DispatchQueue {
            switch self {
            case .Main:
                return DispatchQueue.main
            case .Background:
                return DispatchQueue(label: "com.app.queue",
                                     qos: .background,
                                     target: nil)
            case .LowPriority:
                return DispatchQueue.global(qos: .userInitiated)
            case .HighPriority:
                return DispatchQueue.global(qos: .userInitiated)
            }
        }
    }

    func performOn(_ queueType: QueueType, closure: @escaping () -> Void) {
        queueType.queue.async(execute: closure)
    }

Use it like :

performOn(.Background) {
    //Code
}
Miyamoto answered 19/4, 2019 at 15:34 Comment(0)
T
1

I really like Dan Beaulieu's answer, but it doesn't work with Swift 2.2 and I think we can avoid those nasty forced unwraps!

func backgroundThread(delay: Double = 0.0, background: (() -> Void)? = nil, completion: (() -> Void)? = nil) {

    dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) {

        background?()

        if let completion = completion{
            let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
            dispatch_after(popTime, dispatch_get_main_queue()) {
                completion()
            }
        }
    }
}
Turne answered 3/5, 2016 at 13:55 Comment(0)
H
1

Grand Central Dispatch is used to handle multitasking in our iOS apps.

You can use this code

// Using time interval

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+1) {
    print("Hello World")
}

// Background thread
queue.sync {
     for i in 0..<10 {
          print("Hello", i)
     }
}

// Main thread
for i in 20..<30 {
     print("Hello", i)
}

More information use this link : https://www.programminghub.us/2018/07/integrate-dispatcher-in-swift.html

Hooked answered 2/7, 2018 at 14:15 Comment(0)
M
1

Is there a drawback (when needing to launch a foreground screen afterward) to the code below?

import Foundation
import UIKit

class TestTimeDelay {

    static var connected:Bool = false
    
    static var counter:Int = 0

    static func showAfterDelayControl(uiViewController:UIViewController) {
        NSLog("TestTimeDelay", "showAfterDelayControl")
    }
    
    static func tryReconnect() -> Bool {
        counter += 1
        NSLog("TestTimeDelay", "Counter:\(counter)")
        return counter > 4
    }

    static func waitOnConnectWithDelay(milliseconds:Int, uiViewController: UIViewController) {
        
        DispatchQueue.global(qos: .background).async {
            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + DispatchTimeInterval.milliseconds(milliseconds), execute: {
                waitOnConnect(uiViewController: uiViewController)
            })
        }
    }

    static func waitOnConnect(uiViewController:UIViewController) {

        connected = tryReconnect()
        if connected {
            showAfterDelayControl(uiViewController: uiViewController)
        }
        else {
            waitOnConnectWithDelay(milliseconds: 200, uiViewController:uiViewController)
        }
     }
}   
Mistake answered 25/2, 2022 at 14:52 Comment(1)
simpler???.......Fungus
D
0
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), {
    // Conversion into base64 string
    self.uploadImageString =  uploadPhotoDataJPEG.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.EncodingEndLineWithCarriageReturn)
})
Dominicadominical answered 3/11, 2015 at 7:39 Comment(0)
L
0

in Swift 4.2 this works.

import Foundation

class myThread: Thread
{
    override func main() {
        while(true) {
            print("Running in the Thread");
            Thread.sleep(forTimeInterval: 4);
        }
    }
}

let t = myThread();
t.start();

while(true) {
    print("Main Loop");
    sleep(5);
}
Lyda answered 22/9, 2018 at 12:52 Comment(1)
I wish this wasn't voted down. I have been searching the web for examples on how to create actual threads without GCD in Swift, and it is very difficult to find anything. GCD isn't appropriate for my use case, and I found this little example helpful.Hyaloid
A
0
let backgroundOperation = BlockOperation {
    print("Processing data on a background thread...")
    sleep(2)
    print("Processing complete!")
}

backgroundOperation.completionBlock = {
    // Update UI after the background task finishes (on the main thread)
    DispatchQueue.main.async {
        print("Background task finished, updating UI...")
    }
}

// Add the operation to a queue (e.g., a global background queue)
backgroundOperation.queue = DispatchQueue.global(qos: .background)
backgroundOperation.start()
Afternoons answered 11/4 at 10:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.