How does threading (asynchronous queues) work in Swift? - ios

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

Related

NSAsynchronousFetchRequest - should update be explicitly done on main thread

I am creating an NSAsynchronousFetchRequest which has a completion block inside it.
I have seen various examples where some include using dispatch queue on the main thread and others don't. For example the Ray Wenderlich core data book doesn't call the result on the main thread.
Should I go back on the main thread when executing the result. Initially I thought I had to but now I don't. Some definitive clarity would be great.
fun exampleFetch(_ completionHandler: #escaping () -> () {
let fetchRequest = NSFetchRequest<NSDictionary>(entityName: "Example")
let asyncFetchRequest = NSAsynchronousFetchRequest<NSDictionary>(fetchRequest: fetchRequest) { result in
// DispatchQueue.main.async { // is this needed
completion()
//}
}
managedContext.performChanges {
do {
try self.managedContext.execute(asyncFetchRequest)
} catch let error {
print("error trying to fetch saving objects:", error.localizedDescription)
}
}
}
You should not explicitly call the completion handler on the main queue. Let the caller decide how to handle it. If anything, document that the completion handler will be called on an arbitrary queue. Then the client calling your exampleFetch method knows that it is their responsibility to be sure that process the result on whatever queue it needs.
This gives the client more control.
This also prevents a lot of needless thread switching. A client may call exampleFetch from a background queue and it may want to process the results in the background. If you explicitly put the completion on the main queue, the client then needs to explicitly switch back to a background queue to process the result. That's two needless queue switches and it's wasted effort on the main queue.

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

Swift 3 multithreading using which queue?

I'm going through Stanford CP 193P, looking at a Twitter client.
When a network is called, I assumed it would always be called on the main queue unless invoked on another queue. However without dispatch back onto the main queue (as below) the App does not work as expected - meaning we must not be on the main queue. How?
When tweets are fetched the following closure is used - and to update the UI means that the work needs to be done on the main thread (DispatchQueue.main.async)
request.fetchTweets { [weak self] (newTweets) in
DispatchQueue.main.async {
if request == self?.lastTwitterRequest {
self?.tweets.insert(newTweets, at: 0)
self?.tableView.insertSections([0], with: .fade)
}
}
}
This calls a convenience function that is commented as "handler is not necessarily invoked on the main queue". I can't find anywhere that declares which queue it is invoked on, so I assume it is on the main queue?
// convenience "fetch" for when self is a request that returns Tweet(s)
// handler is not necessarily invoked on the main queue
open func fetchTweets(_ handler: #escaping ([Tweet]) -> Void) {
fetch { results in
var tweets = [Tweet]()
var tweetArray: NSArray?
if let dictionary = results as? NSDictionary {
if let tweets = dictionary[TwitterKey.Tweets] as? NSArray {
tweetArray = tweets
} else if let tweet = Tweet(data: dictionary) {
tweets = [tweet]
}
} else if let array = results as? NSArray {
tweetArray = array
}
if tweetArray != nil {
for tweetData in tweetArray! {
if let tweet = Tweet(data: tweetData as? NSDictionary) {
tweets.append(tweet)
}
}
}
handler(tweets)
}
}
I did not write the Twitter framework, and it appears to have been authored by the Stanford instructor.
You ask:
This calls a convenience function that is commented as "handler is not necessarily invoked on the main queue". I can't find anywhere that declares which queue it is invoked on, so I assume it is on the main queue?
No, you cannot assume it is on the main queue. In fact, it sounds like it's explicitly warning you that it isn't. The only time you can be assured it's on the main queue, is if it explicitly says so.
For example, if the underlying framework is using URLSession, it, by default, does not use the main queue for its completion handlers. The init(configuration:​delegate:​delegate​Queue:​) documentation warns us that the queue parameter is as follows:
An operation queue for scheduling the delegate calls and completion handlers. The queue should be a serial queue, in order to ensure the correct ordering of callbacks. If nil, the session creates a serial operation queue for performing all delegate method calls and completion handler calls.
And for a given framework, it may be completely unrelated to URLSession queue behavior. It might also be using its own queues for completion handlers.
Bottom line, if the framework doesn't explicitly assure you that the closure always runs on the main queue, you should never assume it does. So, yes, in the absence of any assurances to this effect, you'd want to dispatch any UI stuff to the main queue and do the appropriate synchronization for any model objects.
You can, if you have code that must run on a particular thread and you want to make sure this is the case, you can add a dispatchPrecondition to test if it's on the main thread. The behavior of this changes between debug builds and release builds, but it's a quick way of quickly testing if it's using the queue you think it is:
dispatchPrecondition(condition: .onQueue(.main))

Is it normal that CPU usage exceeds 100% using dispatch async in Xcode 7

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.

Ensuring the codes in different sections run in the background queue in iOS

I am new to multithreading in iOS. I need to do three things: get information from the api, parse the information and save to my database. I have these three things in a different files(getAPI,parseAPI and savetoDB). getAPI will call parseAPI and it will in return call savetoDB. I want all three of them to work in background thread.
My question is when I call getAPI, will parseAPI and savetoDB run in the background thread as well? How do I ensure that all three of them run in the background? How do I return the call back to main thread after savetoDB?
Example:
dispatch_queue_t backgroundQueue;
backgroundQueue = dispatch_queue_create("lakesh", NULL);
- (void)startprocess {
dispatch_async(backgroundQueue, ^(void) {
[self getAPI];
});
}
Need some guidance.. Thanks...
If you issue a function on a background thread, all execution will continue on that thread until it finishes or you call back another function on the main thread. I had worries like you in the beginning, so I made myself the following macros:
/// Stick this in code you want to assert if run on the main UI thread.
#define DONT_BLOCK_UI() \
NSAssert(![NSThread isMainThread], #"Don't block the UI thread please!")
/// Stick this in code you want to assert if run on a background thread.
#define BLOCK_UI() \
NSAssert([NSThread isMainThread], #"You aren't running in the UI thread!")
As you can see by the comments, I tend to use these macros at the beginning of methods I want to make sure I'm not using by error in the wrong thread. I've put these macros and more random stuff at https://github.com/gradha/ELHASO-iOS-snippets which you may find useful.
With regards to your question on returning to the main thread, since you are using GCD the best would be to call dispatch_get_main_queue() at the end of your savetoDB with the code you want to run there. If savetoDB is a library function, its entry point should allow passing in the success block you want to run on the main thread when everything finished. This is the pattern used by libraries like https://github.com/AFNetworking/AFNetworking. Note how their examples provide an API where stuff runs in the background and then your code gets called back (usually in the main thread).
Yes, parseAPI and savetoDB will run in the new queue you have created. If you need to modify the UI when the operations are finished, that code must run in the main thread. To do that, get a reference to the main queue and send it some code. For example:
- (void)startprocess {
dispatch_async(backgroundQueue, ^(void) {
[self getAPI];
dispatch_async(dispatch_get_main_queue(), ^{
// Refresh the UI with the new information
});
});
}
Don't forget to dispatch_release your new queue when you're done with it.
Another pattern, used by Cocoa itself in many parts of the framework, is to add callback block to the signatures of your API functions that is invoked when the background operation has ended. This Stack Overflow thread explains how to do that.
Yes of course if getAPI calls parseAPI, the code of parseAPI will execute on the same thread than the one getAPI was executed, so in your example on a background queue.
To return the callback to the main thread at the end, use the same techniques as Apple uses with their completionBlock you can see on multiple Apple APIs : simply pass a block (e.g. dispatch_block_t or void(^)(NSError*) or whatever fits your needs) as a parameter to your getAPI: method which will pass it to parseAPI: which will in turn pass it to savetoDB: and at the end savetoDB: can simply use dipatch_async(dispatch_get_main_queue, completionBlock); to call this block of code (passed from method to method) on the main thread.
Note: for your getAPI you can use Apple's sendAsynchronousRequest:queue:completionHandler: method, that will automatically execute the request in the background then call the completion block on the indicated NSOperationQueue (NSOperationQueue uses GCD's dispatch_queue internally). See documentation on NSOperationQueue, GCD and the Concurrency Programming Guide and all the great detailed guide in Apple doc for more info.
-(void)getAPI:( void(^)(NSError*) )completionBlock
{
NSURLRequest* req = ...
NSOperationQueue* queue = [[NSOperationQueue alloc] init]; // the completionHandler will execute on this background queue once the network request is done
[NSURLConnection sendAsynchronousRequest:req queue:queue completionHandler:^(NSURLResponse* resp, NSData* data, NSError* error)
{
if (error) {
// Error occurred, call completionBlock with error on main thread
dispatch_async(dispatch_get_main_queue(), ^{ completionBlock(error); });
} else {
[... parseAPI:data completion:completionBlock];
}
}];
}
-(void)parseAPI:(NSData*)dataToParse completion:( void(^)(NSError*) )completionBlock
{
... parse datatToParse ...
if (parsingError) {
dispatch_async(dispatch_get_main_queue(), ^{ completionBlock(error); });
} else {
[... savetoDB:dataToSave completion:completionBlock];
}
}
-(void)savetoDB:(id)dataToSave completion:( void(^)(NSError*) )completionBlock
{
... save to your DB ...
// Then call the completionBlock on main queue / main thread
dispatch_async(dispatch_get_main_queue(), ^{ completionBlock(dbError); }); // dbError may be nil if no error occurred of course, that will tell you everything worked fine
}
-(void)test
{
[... getAPI:^(NSError* err)
{
// this code will be called on the main queue (main thread)
// err will be nil if everythg went OK and vontain the error otherwise
}];
}

Resources