I am using realm in my iOS Swift project. Search involve complex filters for a big data set. So I am fetching records on background thread.
But realm can be used only from same thread on which Realm was created.
I am saving a reference of results which I got after searching Realm on background thread. This object can only be access from same back thread
How can I ensure to dispatch code at different time to the same thread?
I tried below as suggested to solve the issue, but it didn't worked
let realmQueue = DispatchQueue(label: "realm")
var orginalThread:Thread?
override func viewDidLoad() {
super.viewDidLoad()
realmQueue.async {
self.orginalThread = Thread.current
}
let deadlineTime = DispatchTime.now() + .seconds(2)
DispatchQueue.main.asyncAfter(deadline: deadlineTime) {
self.realmQueue.async {
print("realm queue after some time")
if self.orginalThread == Thread.current {
print("same thread")
}else {
print("other thread")
}
}
}
}
Output is
realm queue after some time
other thread
Here's a small worker class that can works in a similar fashion to async dispatching on a serial queue, with the guarantee that the thread stays the same for all work items.
// Performs submitted work items on a dedicated thread
class Worker {
// the worker thread
private var thread: Thread?
// used to put the worker thread in the sleep mode, so in won't consume
// CPU while the queue is empty
private let semaphore = DispatchSemaphore(value: 0)
// using a lock to avoid race conditions if the worker and the enqueuer threads
// try to update the queue at the same time
private let lock = NSRecursiveLock()
// and finally, the glorious queue, where all submitted blocks end up, and from
// where the worker thread consumes them
private var queue = [() -> Void]()
// enqueues the given block; the worker thread will execute it as soon as possible
public func enqueue(_ block: #escaping () -> Void) {
// add the block to the queue, in a thread safe manner
locked { queue.append(block) }
// signal the semaphore, this will wake up the sleeping beauty
semaphore.signal()
// if this is the first time we enqueue a block, detach the thread
// this makes the class lazy - it doesn't dispatch a new thread until the first
// work item arrives
if thread == nil {
thread = Thread(block: work)
thread?.start()
}
}
// the method that gets passed to the thread
private func work() {
// just an infinite sequence of sleeps while the queue is empty
// and block executions if the queue has items
while true {
// let's sleep until we get signalled that items are available
semaphore.wait()
// extract the first block in a thread safe manner, execute it
// if we get here we know for sure that the queue has at least one element
// as the semaphore gets signalled only when an item arrives
let block = locked { queue.removeFirst() }
block()
}
}
// synchronously executes the given block in a thread-safe manner
// returns the same value as the block
private func locked<T>(do block: () -> T) -> T {
lock.lock(); defer { lock.unlock() }
return block()
}
}
Just instantiate it and let it do the job:
let worker = Worker()
worker.enqueue { print("On background thread, yay") }
You have to create your own thread with a run loop for that. Apple gives an example for a custom run loop in Objective C. You may create a thread class in Swift with that like:
class MyThread: Thread {
public var runloop: RunLoop?
public var done = false
override func main() {
runloop = RunLoop.current
done = false
repeat {
let result = CFRunLoopRunInMode(.defaultMode, 10, true)
if result == .stopped {
done = true
}
}
while !done
}
func stop() {
if let rl = runloop?.getCFRunLoop() {
CFRunLoopStop(rl)
runloop = nil
done = true
}
}
}
Now you can use it like this:
let thread = MyThread()
thread.start()
sleep(1)
thread.runloop?.perform {
print("task")
}
thread.runloop?.perform {
print("task 2")
}
thread.runloop?.perform {
print("task 3")
}
Note: The sleep is not very elegant but needed, since the thread needs some time for its startup. It should be better to check if the property runloop is set, and perform the block later if necessary. My code (esp. runloop) is probably not safe for race conditions, and it's only for demonstration. ;-)
I'm trying to render some views in background thread to not affect the main thread. That was never a problem before Xcode 9.
DispatchQueue.global(qos: .background).async {
let customView = UIView(frame: .zero)
DispatchQueue.main.async {
self.view.addSubview(customView)
}
}
UIView.init(frame:) must be used from main thread only
This error occurs in the second line.
Update
The Apple UIView Documentation actually says in the Threading Considerations section:
Manipulations to your application’s user interface must occur on the main thread. Thus, you should always call the methods of the UIView class from code running in the main thread of your application. The only time this may not be strictly necessary is when creating the view object itself, but all other manipulations should occur on the main thread.
Xcode 9 has a new runtime Main Thread Checker that detects call to UIKit from a background thread and generate warnings.
I know its meant to generate warnings and not crash the app, but you can try disabling Main Thread Checker for your test target.
I tried this code in a sample project, the debugger paused at the issue (as it is supposed to), but the app didn't crash.
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.global().async {
let v = UIView(frame: .zero)
}
}
Main Thread Entry
You can enter the main thread as follows
DispatchQueue.main.async {
// UIView usage
}
You can use this function
func downloadImage(urlstr: String, imageView: UIImageView) {
let url = URL(string: urlstr)!
let task = URLSession.shared.dataTask(with: url) { data, _, _ in
guard let data = data else { return }
DispatchQueue.main.async { // Make sure you're on the main thread here
imageview.image = UIImage(data: data)
}
}
task.resume()
}
How to use this function?
downloadImage(urlstr: "imageUrl", imageView: self.myImageView)
For Objective C:
dispatch_async(dispatch_get_main_queue(), ^{
// UIView usage
self.view.userInteractionEnabled = YES;
});
I know any updates to UI should be run inside the main queue using the below syntax:
dispatch_async(dispatch_get_main_queue()) {
UI update code here
}
What about these other cases?
In my viewDidLoad(), I have code to stylize the navbar and toolbar, like below:
let nav = self.navigationController?.navigationBar
nav?.barStyle = UIBarStyle.Default
nav?.tintColor = UIColor.blackColor()
nav?.barTintColor = UIColor(red:133.0/255, green:182.0/255, blue:189.0/255, alpha:1.0)
let toolBar = self.navigationController?.toolbar
toolBar?.barTintColor = UIColor(red:231/255, green:111/255, blue:19.0/255, alpha:1.0)
toolBar?.tintColor = UIColor.blackColor()
Should I wrap this code inside the main queue as well?
In my tableView cellForRowAtIndexPath function, should I wrap all the code setting up the UI of each table cell in the main queue as well?
When I present a new modal controller (self.presentViewController(modalController, animated: true, completion: nil), should I wrap this inside the main queue?
The answer to all your questions is "no"
Unless specified in the documentation, all UIKit functions will be called on the main queue.
Generally, you'll need to specifically run on the main queue after calling an async func with a completion handler that runs on a background queue. Something like…
// downloadImage is some func where the
// completion handler runs on a background queue
downloadImage(completion: { image in
DispatchQueue.main.async {
self.imageView.image = image
}
})
Yeah, calling the main queue is something you should only have to do if a function was already being performed asynchronously on a background queue. And that doesn't tend to happen by itself.
Like Ashley says, UIKit methods are automatically called from the main queue – that's just how they are. It stands to reason, then, that some frameworks have methods that automatically call from a background queue.
I know that URLSession's dataTask's resume function automatically executes on a background queue (so that the app isn't slowed down by a web connection), which means the completion handler that executes afterward ALSO works on a background queue. That's why any UI updates that happen in the completion handler certainly require a call on the main queue.
let dataTask = URLSession.shared.dataTask(with: request) {
// begin completion handler
(data, response, error) in
guard error == nil else { print("error \(error.debugDescription)"); return }
guard let data = data else { print("no data"); return }
do {
if let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [[String: Any]] {
print("sending returned JSON to handler")
self.responseHandler.handleAPIResponse(jsonArray: json)
>>>> DispatchQueue.main.async { <<<<
self.tableView.reloadData()
}
}
} catch {
print("get tasks JSONSerialization error")
}
}
dataTask.resume()
My app gets stuck in the icon for about 4-5 seconds when entering foreground, if the networking is bad. In almost all the main loading pages I am fetching images from a server. I have nothing on applicationDidEnterBackground() nor applicationWillEnterForeground().
Here the code in the Main View controller where I have a collection view with images:
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(homeReuseIdentifier, forIndexPath: indexPath)as! HomeViewCell
// Reload the collection view after insert item at indexPath
cvHome.reloadItemsAtIndexPaths([indexPath])
let entry = dataCell.infoCell[indexPath.row]
cell.backImageView.image = nil
let stringURL = "https://.....\(entry.filename)"
let image = UIImage(named: entry.filename)
cell.backImageView.image = image
if cell.backImageView.image == nil {
if let image = dataCell.cachedImage(stringURL) {
cell.backImageView.image = image
} else {
request = dataCell.getNetworkImage(stringURL) { image in
cell.backImageView.image = image
}
}
}
cell.titleLabel.text = entry.title
cell.authorLabel.text = entry.author
// Fix the collection view cell in size
cell.contentView.frame = cell.bounds
cell.contentView.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight]
// To edit the cell for deleting
cell.editing = editing
return cell
}
And this is the getNetworkImage function with Alamofire:
func getNetworkImage(urlString: String, completion: (UIImage -> Void)) -> (ImageRequest) {
let queue = decoder.queue.underlyingQueue
let request = Alamofire.request(.GET, urlString)
let imageRequest = ImageRequest(request: request)
imageRequest.request.response(
queue: queue,
responseSerializer: Request.imageResponseSerializer(),
completionHandler: { response in
guard let image = response.result.value else {
return
}
let decodeOperation = self.decodeImage(image) { image in
completion(image)
self.cacheImage(image, urlString: urlString)
}
imageRequest.decodeOperation = decodeOperation
}
)
return imageRequest
}
I'm not sure if you're networking code is bad since you haven't provided any.
My guess is, fetching the data is taking way too long, and it's blocking the UI. So currently, your app is synchronous. So tasks are executed one at a time. the reason your UI is blocked is because it has to wait for the networking to finish.
What you need to do is perform the fetching in the background, and always keep your UI on the main queue.
If you are calling Synchronous you have to wait till the task is finishing.We can start next task once we finish the current task.When you are fetching image from server make sure that whether you are using Synchronous or not.When you don't wait for a second go with Asynchronous.You don't need to wait while fetch the images from server.You can do other tasks when fetching the images from server.
For this GCD is good one to use.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// Do the back ground process here
......Fetch the image here
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI here
.....Do your UI design or updation design part here
});
});
Global Queue
Asynchronous task will be performed here.
It does not wait.
Asynchronous function does not block the current thread of execution from proceeding on to the next function.
Main Queue
We must always access UI Kit classes on the main thread.
If you don't want to wait a seconds,go with GCD
How to use threading in swift?
dispatchOnMainThread:^{
NSLog(#"Block Executed On %s", dispatch_queue_get_label(dispatch_get_current_queue()));
}];
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
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
})
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
})
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)
}
}
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.
From Jameson Quave's tutorial
Swift 2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
//All stuff here
})
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)
}
}
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
}
}
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
}
}
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
iPhone X: 18.7s / 6.3s / 1.8s / 1.8s / 1.8s
iPhone 7: 4.6s / 3.1s / 3.0s / 2.8s / 2.6s
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.
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:
beforeTask:()->Void invoked on the UI thread just before the task is executed.
backgroundTask: (param:BGParam)->BGResult invoked on the background thread immediately after
afterTask:(param:BGResult)->Void invoked on the UI thread with result from the background task
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
}
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()
}
}
}
}
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
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)
}
}
}
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), {
// Conversion into base64 string
self.uploadImageString = uploadPhotoDataJPEG.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.EncodingEndLineWithCarriageReturn)
})
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);
}