Dispatch Queue inside of Dispatch.main.async - ios

I am currently working on a project that requires me to update a database, and then loop over the database to compare some values. Because of the database update time I have decided to use a delayed call, to give it time to update. Here is the structure of my calls:
//Give database time to update
DispatchQueue.main.asyncAfter(deadline: .now() +5) {
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
closure {
...data processing....
dispatchGroup.leave()
}
//wait for data-processing inside closure to complete
dispatchGroup.wait()
}
This freezes my app. It was my understanding that the closure should run asynchronously and so I was hoping the enter/leave balance would be reached while at dispatchGroup.wait(). Any help for solving this problem would be greatly appreciated thank you!
Note also I occasionally get an EXC_BREAKPOINT error at the line I have indicated, in the following function:
- (void) fireEvent:(id <FEvent>)event queue:(dispatch_queue_t)queue {
if ([event isCancelEvent]) {
FCancelEvent *cancelEvent = event;
FFLog(#"I-RDB065001", #"Raising cancel value event on %#", event.path);
NSAssert(self.cancelCallback != nil, #"Raising a cancel event on a listener with no cancel callback");
dispatch_async(queue, ^{
self.cancelCallback(cancelEvent.error);
});
} else if (self.callback != nil) {
FDataEvent *dataEvent = event;
FFLog(#"I-RDB065002", #"Raising value event on %#", dataEvent.snapshot.key);
dispatch_async(queue, ^{
self.callback(dataEvent.snapshot); <---------
});
}

Dispatch is asynchronous but you are running it on the main thread.
You can simply run it on the background thread, it won't bloack the UI.
DispatchQueue.global(qos: .background).async {
}

Related

How can I invalidate this DispatchWorkItem in Swift? [duplicate]

I am currently playing around with Grand Central Dispatch and discovered a class called DispatchWorkItem. The documentation seems a little incomplete so I am not sure about using it the right way. I created the following snippet and expected something different. I expected that the item will be cancelled after calling cancel on it. But the iteration continues for some reason. Any ideas what I am doing wrong? The code seems fine for me.
#IBAction func testDispatchItems() {
let queue = DispatchQueue.global(attributes:.qosUserInitiated)
let item = DispatchWorkItem { [weak self] in
for i in 0...10000000 {
print(i)
self?.heavyWork()
}
}
queue.async(execute: item)
queue.after(walltime: .now() + 2) {
item.cancel()
}
}
GCD does not perform preemptive cancelations. So, to stop a work item that has already started, you have to test for cancelations yourself. In Swift, cancel the DispatchWorkItem. In Objective-C, call dispatch_block_cancel on the block you created with dispatch_block_create. You can then test to see if was canceled or not with isCancelled in Swift (known as dispatch_block_testcancel in Objective-C).
func testDispatchItems() {
let queue = DispatchQueue.global()
var item: DispatchWorkItem?
// create work item
item = DispatchWorkItem { [weak self] in
for i in 0 ... 10_000_000 {
if item?.isCancelled ?? true { break }
print(i)
self?.heavyWork()
}
item = nil // resolve strong reference cycle of the `DispatchWorkItem`
}
// start it
queue.async(execute: item!)
// after five seconds, stop it if it hasn't already
queue.asyncAfter(deadline: .now() + 5) {
item?.cancel()
item = nil
}
}
Or, in Objective-C:
- (void)testDispatchItem {
dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0);
static dispatch_block_t block = nil; // either static or property
__weak typeof(self) weakSelf = self;
block = dispatch_block_create(0, ^{
for (long i = 0; i < 10000000; i++) {
if (dispatch_block_testcancel(block)) { break; }
NSLog(#"%ld", i);
[weakSelf heavyWork];
}
block = nil;
});
// start it
dispatch_async(queue, block);
// after five seconds, stop it if it hasn't already
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (block) { dispatch_block_cancel(block); }
});
}
There is no asynchronous API where calling a "Cancel" method will cancel a running operation. In every single case, a "Cancel" method will do something so the operation can find out whether it is cancelled, and the operation must check this from time to time and then stop doing more work by itself.
I don't know the API in question, but typically it would be something like
for i in 0...10000000 {
if (self?.cancelled)
break;
print(i)
self?.heavyWork()
}
DispatchWorkItem without DispatchQueue
let workItem = DispatchWorkItem{
//write youre code here
}
workItem.cancel()// For Stop
DispatchWorkItem with DispatchQueue
let workItem = DispatchWorkItem{
//write youre code here
}
DispatchQueue.main.async(execute: workItem)
workItem.cancel()// For Stop
Execute
workItem.perform()// For Execute
workItem.wait()// For Delay Execute

API calls blocks UI thread Swift

I need to sync web database in my coredata, for which I perform service api calls. I am using Alamofire with Swift 3. There are 23 api calls, giving nearly 24k rows in different coredata entities.
My problem: These api calls blocks UI for a minute, which is a long time for a user to wait.
I tried using DispatchQueue and performing the task in background thread, though nothing worked. This is how I tried :
let dataQueue = DispatchQueue.init(label: "com.app.dataSyncQueue")
dataQueue.async {
DataSyncController().performStateSyncAPICall()
DataSyncController().performRegionSyncAPICall()
DataSyncController().performStateRegionSyncAPICall()
DataSyncController().performBuildingRegionSyncAPICall()
PriceSyncController().performBasicPriceSyncAPICall()
PriceSyncController().performHeightCostSyncAPICall()
// Apis which will be used in later screens are called in background
self.performSelector(inBackground: #selector(self.performBackgroundTask), with: nil)
}
An API call from DataSyncController:
func performStateSyncAPICall() -> Void {
DataSyncRequestManager.fetchStatesDataWithCompletionBlock {
success, response, error in
self.apiManager.didStatesApiComplete = true
}
}
DataSyncRequestManager Code:
static func fetchStatesDataWithCompletionBlock(block:#escaping requestCompletionBlock) {
if appDelegate.isNetworkAvailable {
Util.setAPIStatus(key: kStateApiStatus, with: kInProgress)
DataSyncingInterface().performStateSyncingWith(request:DataSyncRequest().createStateSyncingRequest() , withCompletionBlock: block)
} else {
//TODO: show network failure error
}
}
DataSyncingInterface Code:
func performStateSyncingWith(request:Request, withCompletionBlock block:#escaping requestCompletionBlock)
{
self.interfaceBlock = block
let apiurl = NetworkHttpClient.getBaseUrl() + request.urlPath!
Alamofire.request(apiurl, parameters: request.getParams(), encoding: URLEncoding.default).responseJSON { response in
guard response.result.isSuccess else {
block(false, "error", nil )
return
}
guard let responseValue = response.result.value else {
block (false, "error", nil)
return
}
block(true, responseValue, nil)
}
}
I know many similar questions have been already posted on Stackoverflow and mostly it is suggested to use GCD or Operation Queue, though trying DispatchQueues didn't work for me.
Am I doing something wrong?
How can I not block UI and perform the api calls simultaneously?
You can do this to run on a background thread:
DispatchQueue.global(qos: .background).async {
// Do any processing you want.
DispatchQueue.main.async {
// Go back to the main thread to update the UI.
}
}
DispatchQueue manages the execution of work items. Each work item submitted to a queue is processed on a pool of threads managed by the system.
I usually use NSOperationQueue with Alamofire, but the concepts are similar. When you set up an async queue, you allow work to be performed independently of the main (UI) thread, so that your app doesn't freeze (refuse user input). The work will still take however long it takes, but your program doesn't block while waiting to finish.
You really have only put one item into the queue.
You are adding to the queue only once, so all those "perform" calls wait for the previous one to finish. If it is safe to run them concurrently, you need to add each of them to the queue separately. There's more than one way to do this, but the bottom line is each time you call .async {} you are adding one item to the queue.
dataQueue.async {
DataSyncController().performStateSyncAPICall()
}
dataQueue.async {
DataSyncController(). performRegionSyncAPICall l()
}

How to wait first dispatch to finish execution

I have 2 dispatch_async() like this :
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
/* Code here */
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
/* Code here */
}
I want the second dispatch to wait until the first one finish his execution. how i can do that ?
Thank's in advance
I'll give a few solutions, in the order of increasing complexity:
1
The simplest way is to include both code blocks in the same async calls:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
// code block 1
// code block 2
}
2
If you don't know precisely when they will run, for example, code block 1 is triggered when user presses a button and code block 2 is run when user presses another button, use a serial queue:
let serialQueue = dispatch_queue_create("mySerialQueue", DISPATCH_QUEUE_SERIAL)
dispatch_async(serialQueue) {
// code block 1
}
dispatch_async(serialQueue) {
// code block 2
}
3
If your code blocks run asynchronously, like first making a webservice call to authenticate, then making a second call to get the user's profile, you have to implement waiting:
let groupID = dispatch_group_create()
let task1 = session.dataTaskWithRequest(request1) { data, response, error in
// handle the response...
// Tell Grand Central Dispatch that the request is done
dispatch_group_leave(groupID)
}
let task2 = session.dataTaskWithRequest(request2) { data, response, error in
// handle the response...
}
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0)) {
dispatch_group_enter(groupID) // Tell GCD task1 is starting
task1.resume()
dispatch_group_wait(groupID, DISPATCH_TIME_FOREVER) // Wait until task1 is done
task2.resume()
}
4
For anything more complicated, I strongly suggest you learn NSOpereationQueue. There's a WWDC session on it

Waiting for async function to finish using GCD

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()
}

How does threading (asynchronous queues) work in Swift?

ok I am updating this question but left the old one there.
So I have an array that stores the data for different views in a uipageviewcontroller. I need to grab image data in the background. I don't understand how to code this though within an asynchronous task.
Heres the code for the task:
let queue = NSOperationQueue()
queue.addOperationWithBlock() {
// do something in the background
println("background")
self.cards[newIndex].loadImag()
var cardimages = self.cards[newIndex].images
NSOperationQueue.mainQueue().addOperationWithBlock() {
// when done, update your UI and/or model on the main queue
println("update ui")
self.cards[newIndex].images = cardimages
}
}
this is what the .loadImag() function looks like:
func loadImag(){
println("images= \(self.images)")
if self.
location_id != nil && (self.images == nil) {
println("before api call loc_id= \(self.location_id)")
ApiWrapper.getPictures(self.location_id!, completionHandler: self.imagesCallback)
}
}
}
and this is self.imagesCallback code:
private func imagesCallback(cardImagesArray: [CardImage]){
println("images callback id= \(self.location_id)")
self.images = cardImagesArray
}
problem is I am not sure how to put this code inside of the operation cue since the function must have a callback. How can I get the operation queue working so that it updates the self.card array in the uipageviewcontroller?
OLD QUESTION_________________:
So I have this line of code I need to run concurrently in a different thread than the main thread. When I add it to the main queue like so:
var queue = dispatch_get_main_queue()
dispatch_async(queue, {
self.cards[newIndex].loadImage()
})
doing this it works fine but doesn't seem to run concurrently. When I change the queue to concurrent like this:
dispatch_async(DISPATCH_QUEUE_CONCURRENT, {
self.cards[newIndex].loadImage()
})
The app crashes saying "EXC_BAD_ACCESS". What am I doing wrong here? Also when I run the self.cards[newIndex].loadImage() function in a different concurrent thread will this update the values in the main thread?
you shouldn't use GCD unless you want to explicitly use functionality which is only available on GCD. For your case it is more beneficial (and cleaner in code) to use NSOperationQueue. NSOperationQueue uses GCD in the background and is more secure (less ways to mess up)
let queue = NSOperationQueue()
queue.addOperationWithBlock() {
// do something in the background
NSOperationQueue.mainQueue().addOperationWithBlock() {
// when done, update your UI and/or model on the main queue
}
}
You can also read through the Apple Concurrency Programming Guide
The guide is using examples with Objective-C but API is basically the same for Swift.
It also might look strange but with the addOperationWithBlock() I used something called "trailing closure" you can read here about it
Can you paste the whole code so we can see what are you doing?
Below is very basic code snippet. This is basically how concurrency works in Swift.
let qos = Int(QOS_CLASS_USER_INITIATED.value)
dispatch_async(dispatch_get_global_queue(qos, 0), { () -> Void in
// Put your code here to work in the background
dispatch_async(dispatch_get_main_queue(), { () -> Void in
// Put your code here when the process is done and hand over to the main thread.
// Ex. self.cards[newIndex].loadImage()
})
})
You need to use dispatch_get_global_queue . Try something like:
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue, {self.cards[newIndex].loadImage()})
dispatch_get_main_queue(), as you were trying, runs on the UI/main thread, which is why you saw the behavior you did.
To answer the second part of your question, If loadImage() is modifying the UI, you don't want to do that from a background thread. It must be done from the main/UI thread. A typical idiom would be, from the main thread do:
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue, {
<code to load/prepare images>
dispatch_async(dispatch_get_main_queue(), {
<code to update UI elements>
})
})

Resources