Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning value while dispatching to the main thread

I have a function func getValue() -> Bool that's called from a background thread. This is intentional and also required. Now, the getValue() needs to execute something on the main thread, in this case it needs to access UIApplication.shared.canOpenURL, which must be run on the main queue.

This is my current function:

func getValue() -> Bool {
    guard let url = URL(string: "someurl") else { return false }
    return UIApplication.shared.canOpenURL(url)
}

How can I convert that function to a thread safe one, namely to make sure it always runs on the main thread, without

  • calling the function from the main thread to begin with
  • refactoring the function to return the value in a closure

I've tried this:

// This causes a deadlock, see https://stackoverflow.com/a/42484670/1531270
func getValue() -> Bool {
    var flag = false

    let group = DispatchGroup()
    group.enter()
    DispatchQueue.main.async {
        if let url = URL(string: "someurl"), UIApplication.shared.canOpenURL(url) {
            flag = true
        }
        group.leave()
    }
    group.wait()

    return flag
}

and this:

// This crashes with EXC_BREAKPOINT (SIGTRAP) dispatch_sync called on queue already owned by current thread
func getValue() -> Bool {
    return DispatchQueue.main.sync {
        guard let url = URL(string: "someurl") else { return false }
        return UIApplication.shared.canOpenURL(url)
    }
}

but neither of them works. Any ideas?

like image 655
imas145 Avatar asked Aug 18 '18 14:08

imas145


People also ask

What does DispatchQueue main Async do?

To summarize, DispatchQueue. async allows you to schedule work to be done using a closure without blocking anything that's ongoing. In most cases where you need to dispatch to a dispatch queue you'll want to use async .

What is DispatchQueue Global () async?

DispatchQueue. global() runs these kind of tasks in background threads. You can tell the queue about how important your task is, so that DispatchQueue can prioritize your task. You can do this by providing Quality-of-Service information.

What is the main thread in Swift?

The main thread is the one that starts our program, and it's also the one where all our UI work must happen. However, there is also a main queue, and although sometimes we use the terms “main thread” and “main queue” interchangeably, they aren't quite the same thing.

What is the use of DispatchQueue in Swift?

A dispatch queue that is bound to the app's main thread and executes tasks serially on that thread. A dispatch queue that executes tasks concurrently using threads from the global thread pool. A dispatch queue that executes tasks serially in first-in, first-out (FIFO) order.


1 Answers

You're looking for a semaphore - try this:

DispatchQueue.global(qos: .background).async {
    var value: Bool? = nil
    let semaphore = DispatchSemaphore(value: 0)
    DispatchQueue.main.async {
        let alert = UIAlertController(title: "Choose one", message: "Take your time, I'll wait", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "true", style: .default, handler: { _ in
            value = true
            semaphore.signal()
        }))
        alert.addAction(UIAlertAction(title: "false", style: .default, handler: { _ in
            value = false
            semaphore.signal()
        }))
        self.present(alert, animated: true, completion: nil)
    }
    semaphore.wait()
    print("Choice: \(value!)")
}

Or to use your example from above:

func getValue() -> Bool {
    var flag = false
    let semaphore = DispatchSemaphore(value: 0)

    DispatchQueue.main.async {
        if let url = URL(string: "someurl"), UIApplication.shared.canOpenURL(url) {
            flag = true
            semaphore.signal()
        }
    }
    semaphore.wait()
    return flag
}
like image 178
sam-w Avatar answered Sep 19 '22 20:09

sam-w