I'm currently testing this code in the Xcode 10 playground (Swift 5):
func one() {
let test = "bla"
two(test, completion: { (returned) in
print(returned)
})
}
func two(_ test: String, completion: @escaping (_ returned: String?) -> Void) {
DispatchQueue.global(qos:.background).async {
if !test.isEmpty {
//Some slow stuff
DispatchQueue.main.async {
return completion("hi!")
}
}
//Also some slow stuff
DispatchQueue.main.async {
return completion(nil) //can't have this in "else"!
}
}
}
one()
The problem is that both "hi" and "nil" are printed.
If I get rid of the threading, it works fine but with it it seems like it gets to the second DispatchQueue.main.async
before the first has the chance to return.
There's a lot more stuff going on in the "Some slow stuff" if
in my actual code but I can't rely on that taking long enough to return before the second return is called too.
How do I accomplish this: Have the function run in a background thread but return only once on the main thread (like code without threading normally would)?
I believe your goal is to only call the completion
handler once, and when you do you are done. In that case, call return
in the .background
thread after queueing the completion call on the main thread:
func two(_ test: String, completion: @escaping (_ returned: String?) -> Void) {
DispatchQueue.global(qos:.background).async {
if !test.isEmpty {
//Some slow stuff
// notify main thread we're done
DispatchQueue.main.async {
completion("hi!")
}
// we are done and don't want to do more work on the
// background thread
return
}
//Also some slow stuff
DispatchQueue.main.async {
completion(nil)
}
}
}