this is a hard one to explain. I am creating a serial queue for handling some work in my app. Imagine that i do something like this:
dispatch_async(myQueue, { () -> Void in
self.SendSMS();
});
dispatch_async(myQueue, { () -> Void in
self.SendEmail();
});
Now what i would like to do is to only call the self.SendEmail after a delegate(SendSMS delegate) finishes its work.
Is there a simple way to do this?
Many thanks
Assuming that SendSMS is an asynchronous method, I'd advise changing SendSMS to take a completion handler closure:
// define property to hold closure
var smsCompletionHandler: (()->())?
// when you initiate the process, squirrel away the completion handler
func sendSMSWithCompletion(completion: (()->())?) {
smsCompletionHandler = completion
// initiate SMS
}
// when the SMS delegate method is called, call that completion closure
func messageComposeViewController(controller: MFMessageComposeViewController!, didFinishWithResult result: MessageComposeResult) {
// do whatever you want when done
// finally, call completion handler and then release it
smsCompletionHandler?()
smsCompletionHandler = nil
}
Thus, you'd call it like so, putting the sendEmail inside the completion closure of sendSMS:
self.sendSMSWithCompletion() {
self.sendEmail()
}
I don't know what your sendSMS and sendEmail are doing, but if you're calling the MessageUI framework, you'd generally do that on the main queue. But if you really need to do the above on your dedicated queue, then feel free to dispatch it there. But hopefully this illustrates the concept: (a) supply completion handler closure; (b) save it so your delegate can call it; and (c) when delegate is called, use that closure property and then reset it.
one way to do it, and it works is to put:
dispatch_async(myQueue, { () -> Void in
self.SendEmail();
});
at the end of the delegate. But i dont know if this is the only way to do this.
Cheers
Yes, you can do it, in next steps:
// create tasks group handle
let taskGroup = dispatch_group_create()
let mainQueue = dispatch_get_main_queue()
// write your blocks in needed order
dispatch_group_async(taskGroup, mainQueue) { [weak self] in
// execute your code
// don't forget to use self with optional, i.e.: self!.property or function
self!.SendSMS()
}
dispatch_group_async(taskGroup, mainQueue) { [weak self] in
self!.SendEmail()
}
// and of course you need to catch completion of this task group
dispatch_group_notify(taskGroup, mainQueue) {
println("All work is done, milord!")
}
UPD. The solution above is about asynchronous execution without order, as named and one of two can be completed earlier than declared order. You need to use dependencies as solution of continuously execution. I'm talking about ordering in multithreading, not about completion closures or executor pattern.
Notice, that there're more than one case to do this. One of them – below:
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue) {
dispatch_sync(queue) {[weak self] in
self?.SendSMS()
}
dispatch_sync(queue) {[weak self] in
self?.SendEmail()
// here you need to call your completion of success function in main thread
}
}
Be aware, that code in your functions must exists in the same queue and use synchronous method for server requests. But this is another story ;)
Related
I have implemented following completion block, one block is completed and then I update UI and object accordingly.
func doPaging() {
fetchProducts(page: pageNumber , completion: { success in
if let products = success as? Products
{
DispatchQueue.main.async {
self.products.append(contentsOf:products)
self.isWating = false;
self.productTableView.reloadData()
}
}
})
}
func fetchProducts(page: Int, completion: #escaping ((AnyObject) -> Void)) {
// URLSession call here
}
However, the following approach clearly shows restful call will happen in background thread and once it is completed, then update UI and objects.
func doPaging() {
DispatchQueue.global(qos: .background).async {
// Background Thread
fetchProducts()
DispatchQueue.main.async {
self.pageNumber += 1
self.productTableView.reloadData()
self.isWating = false
}
}
}
func fetchProducts(page: Int) {
// URLSession call here
}
I am confused between completion block method vs. DispatchQueue.
Which one is recommended?
In the first approach, you call a method fetchProducts() which internally uses NSURLSession. REST call using NSURLSession runs in background and on completion of the REST call, the completion of the task will be called. In that completion, you call your completion handler of fetchProducts(). This approach seems fine to me.
In the second approach, you use global background queue and asynchronously call NSURLSession APIs (I assume so), and don’t wait for the call to complete. The code on main queue will be instantly called and at this point the NSURLSession task may or may not have been completed.
So, this approach is problematic.
First method seems OK as long as you fetchProducts asynchronously. In fetchProducts() , if you call the completion block in the main queue you won't even need to get main queue again in the doPaging() method.
In your second method, you are calling fetchProducts() in a global (concurrent) queue. Although global queues start each task in the order they were added to queue, they run tasks concurrently. And since fechtProduct() takes time, your code block that contains self.pageNumber += 1 executed before even fetchProduct's URLSession is started. So, this approach won't work.
Completion block and Dispatch Queue are two different concepts.
Completion block is used when your function perform actions takes time to run, and need to return back and run some code even the functions has "ended". For example,
func networkCall(foo: Int, completion:#escaping (_ result:Bool)-> Void))
func otherFunc(){...}
func A(){
networkCall(foo:1){ (success) in
// handle your stuff
}
otherFunc()
}
When you run A(), it first run networkCall(), however networkCall() may takes time to run the network request and the app moved on to run otherFunc(). When the network request is done, networkCall() can call it's completion block so that A() can handle it again.
Dispatch Queue is the threading stuff safely encapsulated by Apple. Network request can be performed in Main thread as well, but it will be blocking other functions.
A common practice is to call Network request in background queue
DispatchQueue.global(qos: .background).async and call completion block after finished. If anything needs to be updated in main thread like UI, do it in the DispatchQueue.main.async
In Xcode 9 / Swift 4 using Google APIs Client Library for Objective-C for REST: why does service.executeQuery return thread completion notification before the thread completes?
I have been trying various ways but I am stuck with the following code where the notification is returned before the thread completes. See below the code, the actual output and what I would expect to see (notification comes once the thread has complete).
What am I doing wrong?
Thanks
func myFunctionTest () {
let workItem = DispatchWorkItem {
self.service.executeQuery(query,
delegate: self,
didFinish: #selector(self.displayResultWithTicket2b(ticket:finishedWithObject:error:))
)
}
let group = DispatchGroup()
group.enter()
group.notify(queue: service.callbackQueue) {
print("************************** NOTIFY MAIN THREAD *************************************")
}
service.callbackQueue.async(group: group) {
workItem.perform()
}
group.leave()
}
#objc func displayResultWithTicket2b(ticket : GTLRServiceTicket,
finishedWithObject messagesResponse : GTLRGmail_ListMessagesResponse,
error : NSError?) {
//some code to run here
print("************************** 02.displayResultWithTicket2b ***************************")
}
Output
************************** NOTIFY MAIN THREAD *************************************
************************** 02.displayResultWithTicket2b ***************************
What I would expect = Thread notification comes when thread has completed
************************** 02.displayResultWithTicket2b ***************************
************************** NOTIFY MAIN THREAD *************************************
The problem is that you're dealing with an asynchronous API and you're calling leave when you're done submitting the request. The leave() call has to be inside the completion handler or selector method of your executeQuery call. If you're going to stick with this selector based approach, you're going to have to save the dispatch group in some property and then have displayResultWithTicket2b call leave.
It would be much easier if you used the block/closure completion handler based rendition of the executeQuery API, instead of the selector-based API. Then you could just move the leave into the block/closure completion handler and you'd be done. If you use the block based implementation, not only does it eliminate the need to save the dispatch group in some property, but it probably eliminates the need for the group at all.
Also, the callback queue presumably isn't designed for you to add your own tasks. It's a queue that the library will use the for its callbacks (the queue on which completion blocks and/or delegate methods will be run). Just call executeQuery and the library takes care of running the callbacks on that queue. And no DispatchWorkItem is needed:
session.executeQuery(query) { ticket, object, error in
// do whatever you need here; this runs on the callback queue
DispatchQueue.main.async {
// when you need to update model/UI, do that on the main queue
}
}
The only time I'd use a dispatch group would be if I was performing a series of queries and needed to know when they were all done:
let group = DispatchGroup()
for query in queries {
group.enter()
session.executeQuery(query) { ticket, object, error in
defer { group.leave() }
// do whatever you need here; this runs on the callback queue
}
}
group.notify(queue: .main) {
// do something when done; this runs on the main queue
}
How can I prevent a block of code to be repeatedly accessed from the same thread?
Suppose, I have the next code:
func sendAnalytics() {
// some synchronous work
asyncTask() { _ in
completion()
}
}
I want to prevent any thread from accessing "// some synchronous work", before completion was called.
objc_sync_enter(self)
objc_sync_exit(self)
seem to only prevent accessing this code from multiple threads and don't save me from accessing this code from the single thread. Is there a way to do this correctly, without using custom solutions?
My repeatedly accessing, I mean calling this sendAnalytics from one thread multiple times. Suppose, I have a for, like this:
for i in 0...10 {
sendAnalytics()
}
Every next call won't be waiting for completion inside sendAnalytics get called (obvious). Is there a way to make the next calls wait, before completion fires? Or the whole way of thinking is wrong and I have to solve this problem higher, at the for body?
You can use a DispatchSemaphore to ensure that one call completes before the next can start
let semaphore = DispatchSemaphore(value:1)
func sendAnalytics() {
self.semaphore.wait()
// some synchronous work
asyncTask() { _ in
completion()
self.semaphore.signal()
}
}
The second call to sendAnalytics will block until the first asyncTask is complete. You should be careful not to block the main queue as that will cause your app to become non-responsive. It is probably safer to dispatch the sendAnalytics call onto its own serial dispatch queue to eliminate this risk:
let semaphore = DispatchSemaphore(value:1)
let analyticsQueue = DispatchQueue(label:"analyticsQueue")
func sendAnalytics() {
analyticsQueue.async {
self.semaphore.wait()
// some synchronous work
asyncTask() { _ in
completion()
self.semaphore.signal()
}
}
}
I'm a beginner in swift 2, and I'm trying to make my program blocks while showing only a progress spinner until some operation finishes, I made that code snippet in a button with the action "touch up inside", my problem is that while debugging,Xcode 7 CPU usage jumps to 190 % once I tap my button and keeps high until the flag changes its value, Is it normal that CPU usage jumps like that?, also Is it a good practice to use the following snippet or shud i use sleep or some other mechanism inside my infinite loop?
let queue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(self.queue2) { () -> Void in
while(flag == true)
{
//wait until flag sets to false from previous func
}
self.dispatch_main({
//continue after the flag became false
})
This is a very economical completion handler
func test(completion:() -> ())
{
// do hard work
completion()
}
let queue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue2) {
test() {
print("completed")
}
}
or with additional dispatch to the main queue to update the UI
let queue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue2) {
test() {
print("completed")
dispatch_async(dispatch_get_main_queue()) {
// update UI
}
}
}
This is totally wrong approach as you are using while loop for waiting. You should use Completion Handler to achieve this kind of stuff.
Completion handlers are callbacks that allow a client to perform some action when a framework method or function completes its task. Often the client uses a completion handler to free state or update the user interface. Several framework methods let you implement completion handlers as blocks (instead of, say, delegation methods or notification handlers).
Refer Apple documentation for more details.
I suppose you have a sort of class which manages these "some operation finishes".
When you finish your operations you can comunicate by completion handler or delegation. In the meanwhile you can disable the user interaction of your UI until the end of these operations.
If you provide more informations about your background operations I can add some snippets of code.
I have an async function that queries Parse. I need to wait until all objects from the Parse query have returned before calling my second function. The problem is, I'm using:
var group: dispatch_group_t = dispatch_group_create()
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) { () -> Void in
asyncFunctionA() // this includes an async Parse query
}
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) { () -> Void in
asyncFunctionB() // must be called when asyncFunctionA() has finished
}
...but, asyncFunctionB() is getting called before I even have any objects appended to my arrays in asyncFunctionA(). Isn't the point of using GCD notify to observe the completion of a prior function? Why isn't that working here?
Just like Parse employs the concept of completion block/closures, you need to do the same in your asyncFunctionA:
func asyncFunctionA(completionHandler: () -> ()) {
// your code to prepare the background request goes here, but the
// key is that in the background task's closure, you add a call to
// your `completionHandler` that we defined above, e.g.:
gameScore.saveInBackgroundWithBlock { success, error in
if (success) {
// The object has been saved.
} else {
// There was a problem, check error.description
}
completionHandler()
}
}
Then you could do something like your code snippet:
let group = dispatch_group_create()
dispatch_group_enter(group)
asyncFunctionA() {
dispatch_group_leave(group)
}
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
self.asyncFunctionB()
}
Note, if function A was really using Parse's asynchronous methods, then there's no need to use dispatch_async there. But if you need it for some reason, feel free to add that back in, but make sure the dispatch_group_enter occurs before to dispatch to some background thread.
Frankly, I'd only use groups if I had a whole bunch of items added to this group. If it really was just B waiting for single call to A, I'd retire the groups entirely and just do:
asyncFunctionA() {
self.asyncFunctionB()
}