How to know when the last async server call ended [duplicate] - ios

This question already has an answer here:
Swift Closures in for loop
(1 answer)
Closed 5 years ago.
I'm wondering what's the best approach to handle asynchronous calls in a for loop, so we know when the last call was made/ended.
The issue here is that the for loop is was faster than the server calls, so the calls will be queued and how can I ensure the last
was made and ended?
Take the following pseudo example:
someData.forEach({(object) in
self.myFunc(object)
})
fileprivate func myFunc(object: object) {
doesSomeServerAsyncCallWithObjectAndCompletion(object) {(success, desiredData) in
// HOW TO KNOW HERE THAT THIS WAS THE LAST CALL REQUESTED??
}
}

Use DispatchGroup
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
longRunningFunction { dispatchGroup.leave() }
dispatchGroup.enter()
longRunningFunctionTwo { dispatchGroup.leave() }
dispatchGroup.notify(queue: .main) {
print("Both functions complete 👍")
}
For example
let queue = DispatchQueue(label: "com.allaboutswift.dispatchgroup", attributes: .concurrent, target: .global())
let group = DispatchGroup()
group.enter()
queue.async (group: group) {
print("doing stuff again 1")
group.leave()
}
group.enter()
queue.async (group: group) {
print("doing more stuff again 2 ")
sleep(3)
group.leave()
}
group.notify(queue: DispatchQueue.main) {
print("done doing stuff again 3")
}

Related

Swift DispatchGroup notify before task finish

I'm using DispatchGroup to perform a task, but group.notify is being called before the task is completed.
My code:
let group = DispatchGroup()
let queueImage = DispatchQueue(label: "com.image")
let queueVideo = DispatchQueue(label: "com.video")
queueImage.async(group: group) {
sleep(2)
print("image")
}
queueVideo.async(group: group) {
sleep(3)
print("video")
}
group.notify(queue: .main) {
print("all finished.")
}
Logs:
all finish.
image
video
Update: The question above actually runs correctly as is (as rmaddy pointed out!)
I'm saving this wrong answer below in case others get confused about DispatchQueue's async(group:) methods behavior, since Apple's swift doc on it is currently lousy.
The group's enter() needs to be called before each call to async(), and then the group's leave() needs to be called at end of each async() block, but within the block. It's basically like a refcount that when it reaches zero (no enters remaining), then the notify block is called.
let group = DispatchGroup()
let queueImage = DispatchQueue(label: "com.image")
let queueVideo = DispatchQueue(label: "com.video")
group.enter()
queueImage.async(group: group) {
sleep(2)
print("image")
group.leave()
}
group.enter()
queueVideo.async(group: group) {
sleep(3)
print("video")
group.leave()
}
group.notify(queue: .main) {
print("all finished.")
}
Generic answer : (Swift 5)
let yourDispatchGroup = DispatchGroup()
yourDispatchGroup.enter()
task1FunctionCall {
yourDispatchGroup.leave() //task 1 complete
}
yourDispatchGroup.enter()
task2FunctionCall {
yourDispatchGroup.leave() //task 2 complete
}
.. ..
yourDispatchGroup.enter()
tasknFunctionCall {
yourDispatchGroup.leave() //task n complete
}
dispatchGroup.notify(queue: .main) {
//This is invoked when all the tasks in the group is completed.
}
If your DispatchGroup is a lazy var, try to not call the notify method inside the initialization code block.
lazy var dispatchGroup: DispatchGroup = {
let dispatchGroup = DispatchGroup()
// not call here dispatchGroup.notify(...
return dispatchGroup
}()
You need to call all the enter methods before the notify method:
dispatchGroup.enter()
dispatchQueue.async(group: dispatchGroup) {
// ...
self.dispatchGroup.leave()
}
dispatchGroup.notify(queue: .main) {
print("all finished.")
}

Accessing main queue via DispatchGroup vs. DispatchQueue

I'm using DispatchGroup in a class that runs on a background thread. Occasionally, I need to update the UI, so I call the following code:
dispatchGroup.notify(queue: .main) {
self.delegate?.moveTo(sender: self, location: location)
self.delegate?.updateLabel(sender: self, item: self.currentItem)
}
Unfortunately, nothing happens. However, if I call the same code, via DispatchQueue.main.async { }, like so:
DispatchQueue.main.async {
self.delegate?.moveTo(sender: self, location: location)
self.delegate?.updateLabel(sender: self, item: self.currentItem)
}
...the delegate call gets made. I was under the impression dispatchGroup.notify(queue: .main) { } is equivalent to DispatchQueue.main.async { }.
Why are these not the same?
Is your dispatchGroup empty (i.e. have no blocks running) at the time when you call notify(queue:)? If not, as documentation states dispatchGroup.notify(queue:)
Schedules a work item to be submitted to a queue when a group of previously submitted block objects have completed.
Which means that your closure will be executed only after the last leave() call, when the group becomes empty. And, of course, enter()s and leave()s must be balanced.
Consider the following example:
let group = DispatchGroup()
group.enter()
someLongRunningTask() {
// completion callback
group.leave()
}
group.enter()
anotherLongRunningTask() {
// completion callback
group.leave()
}
group.notify(queue: .main) {
print("all set")
}
In this example all set will be printed only after two callbacks with group.leave() execute.
On the other hand, DispatchQueue.main.async() immediately submits the block to the target queue but it will not necessarily start right after that – there could be running, for example, async block with the .barrier flag.
Update: Implementation of the example above using DispatchQueue (hopefully, it makes things clear):
let group = DispatchGroup()
group.enter()
someLongRunningTask() {
// completion callback
group.leave()
}
group.enter()
anotherLongRunningTask() {
// completion callback
group.leave()
}
group.wait() // waits synchronously for the submitted work to complete
DispatchQueue.main.async {
print("all set")
}

How to know when 2 parallel threads request is completed in iOS

for eg. I have two separate requests and both running in different threads then how do I get to know that both are done with their request and completed.
See below implementation , This can help you in detail:
let queue = DispatchQueue(label: "com.learn.swift", attributes: .concurrent, target: .main)
let group = DispatchGroup()
group.enter()
queue.async (group: group) {
print("1st Thread")
group.leave()
}
group.enter()
queue.async (group: group) {
print("2nd Thread")
group.leave()
}
group.notify(queue: DispatchQueue.main) {
print("All done")
}
Output:
1st Thread
2nd Thread
All done
You can use dispatch_group here. Enter into group using dispatch_enter and leave by using dispatch_leave. Both can notify by dispatch_group_notify

Dispatch Group not working for completion block

Trying to get return reply objects from a network call. The session is a class that is using the star scream API. I just can't seem to get this to work. It's only printing out 1 set of results which is the from the first id. What am I missing here?
let myGroup = DispatchGroup()
for i in 0 ..< marketIds.count {
myGroup.enter()
self.session.retrieve(withMethod: MarketKeys.key, withParameters: [MarketKeys.id: marketIds[i]], completion: { (results, error) in
print("results \n")
print(results!)
myGroup.leave()
})
}
myGroup.notify(queue:.main) {
print("Done")
}
This article gives you a quick reference guide to simple DispatchGroup use.
An example:
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
longRunningFunction { dispatchGroup.leave() }
dispatchGroup.enter()
longRunningFunctionTwo { dispatchGroup.leave() }
dispatchGroup.notify(queue: .main) {
print("Both functions complete 👍")
}
The notify function is called when all items in the queue have been processed and allows you to react to this accordingly. So the example above will run two long running tasks and then will output "Both functions complete 👍"
Add this so notification to group can be sent
myGroup.notify(queue: .main) {
print("Both functions complete 👍")
}

Swift: Deadlock with DispatchGroup

I've got some issue with CoreData concurrency.
I can't do context.perform while a destination thread is blocked with DispatchGroup.
Here is a simple example which shows the issue:
func upload(objects: [NSManagedObject]) {
let group = DispatchGroup()
for object in objects {
group.enter()
upload(object) {
group.leave()
}
}
group.wait() // current thread is blocked here
someAdditionalWorkToDoInSameThread()
}
func upload(object: NSManagedObject, completion: ()->()) {
let context = object.managedObjectContext
performAlamofireRequest(object) {
context.perform {
// can't reach here because the thread is blocked
update(object)
completion()
}
}
}
Please, help me to reimplement this properly. Thanks.
Using notify on dispatch group instead of wait, should resolve your issues.
Calling wait() blocks current thread for completion of previously submitted work.
notify(queue:execute:) will notify the queue that you passed as argument that the group task has been completed.
func upload(objects: [NSManagedObject], completion: ()->()) {
let group = DispatchGroup()
for object in objects {
group.enter()
upload(object) {
group.leave()
}
}
group.notify(queue: DispatchQueue.main) {
completion()
}
}

Resources