Is asynchronous code always run on a different thread? - ios

I really think that if code in Swift in iOS runs asynchronously, then it is by the nature of itself that it runs on a separate thread than the thread the code is called from - usually the main thread. It seems obvious to me, but I am uncertain and would like to receive verification of this. I can't think of any conditions in which an asynchronous code would run on the same code that it was called from. It seems almost a certainty by definition that an asynchronous code runs on a different thread than the thread it was called from.
What do you think? Would you agree and verify this?
I ask this question while trying to understand the #escaping keyword as it applies to completion handlers. The Swift documentation on Closuressays that the #escaping keyword causes the completion handler that is designated as escaping to run asynchronously and to run after the function (that receives the completion closure as an argument) finishes running. The documentation, however, does not say whether the escaping completion handler runs on a different thread or on the current thread - which would be the main/UI thread.
I'm trying to hunt where a run-time error is coming from. The error message says: "Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread."
It might be coming from the completion handler of CNContactStore requestAccess(for:completionHandler:), but I need to know if #escaping causes the closure it applies to to run on a different thread than the thread the closure was called from, since the completionHandler parameter is defined as escaping by the #escaping keyword in the definition of CNContactStore requestAccess(for:completionHandler:).
Is the error I'm getting coming from code inside the completion handler of that function that modifies the layout engine or any completion handler marked as escaping?
These posts on stackoverflow helped me clarify my question, but they do not answer my question:
Does Asynchronous code run in the UI thread or a new/different thread to not block the UI?
async - stay on the current thread?

#escaping does NOT determine which thread the closure is ran on. The documentation does.
For that specific function (https://developer.apple.com/documentation/contacts/cncontactstore/1402873-requestaccess/):
The system executes completionHandler on an arbitrary queue. It is recommended that you use CNContactStore instance methods in this completion handler instead of the UI main thread.
If you are doing any sort of UI work inside of the completionHandler callback, it means you MUST call DispatchQueue.main.async { update_my_UI_here() } to execute your code safely on the main thread.
Example:
requestAccess(for: .contacts) { [weak self] permissionGranted, error in
//All code in here is ran on an ARBITRARY background queue
if let error = error {
log(error)
return
}
//CNContactStore - Any CNContactStore functions should run here, and not inside `DispatchQueue.main`
guard let self = self else { return }
// Any UI updates or any code that interacts with `UI*` or constraints, must be done on main
DispatchQueue.main.async {
self.update_my_ui_here(permisionGranted) //Safely update UI from the main queue
}
}
Marking a function as #escaping just means that it may POTENTIALLY be called at a later time or stored as a variable somewhere. That's all it means. It does NOT mean that is will be ran on the same OR a different thread. It doesn't have any guarantees about threads and it doesn't have any guarantees about when it will run.
After all, DispatchQueue.main.async(completion: #escaping () -> Void) has an escaping parameter, and yet it always runs the completion on the exact same main thread. main is the only queue with such guarantee, regardless of whether the parameter is escaping or not.

Related

Understanding escaping closures Swift

Need help with escaping closures.
I understand that the definition of escaping closures is If a closure is passed as an argument to a function and it is invoked after the function returns, the closure is escaping.
I believe there are a few scenarios where escaping closures are necessary.
Setting an outside variable as the passing closure
I understand this because the variable lives even after the function returns.
Asynchronous operations
Things like Dispatch.main.async{} or Network Request.This is the part the I don't quite understand. I understand that Dispatch.main.async{} runs on a different thread and that async operations take a while to complete hence the name async. However, I cannot understand how these operations outlive the scope of the function.
When you call DispatchQueue.main.async{} you are creating a closure (a block in Objective-C) that is sent to the given dispatch queue. Your function will then continue running after the async call and could end before the block has a chance to run on the DispatchQueue. The closure has "escaped"!
The code in the block will actually be run when the block reaches the front of its DispatchQueue
The async doesn't mean that the operation will take a long time, it means that this function (the one calling async) will not pause to wait on the block to complete.
(if you DID want to pause and wait for the operation to complete, you could call sync instead of async).
When you do asynchronous work it might escape the current scope cause, by definition, it won't be waited for its completion.
This is also why the terminology completion is used for those closures adopted in asynchronous code for the delivery of results.
Here's an example, assuming queue is a DispatchQueue and someHugeWork() -> R is a synchronous function which returns a result R:
func doItSync() -> R {
var result: R!
queue.sync {
result = someHugeWork()
}
// the function has to wait for the code submitted synchronously
// on the queue.
return result
}
func doItAsync(completion: #escaping (R) -> Void) {
queue.async {
let result = someHugeWork()
completion(result)
}
// work was submitted asynchronously on the queue, hence the function
// just returns, therefore the completion closure might escape this
// scope.
}
Now, obviously in this case doItSync() does not really make sense, since it'll block the current thread until someHugeWork() has executed on a different queue. That is also why you can return the result from its scope: it waits for it. I've only added this to let you see the difference between synchronous code and asynchronous code for returning a result.
On the other hand doItAsync(completion:) just returns immediately without having to wait for someHugeWork() to complete on a different queue, hence it won't block the current thread.
Since you've submitted this work asynchronously on the queue you now have no control about when it will be executed and that is also why your only way to return the result from someHugeWork() is by using a completion closure, which has to be captured inside the work item submitted asynchronously… If you think about it for a moment it'll be as if such completion closure is stored to be retrieved by the queue at a later time when it will executes the work item submitted asynchronously (you said in your question that you did got the concept of storing a closure).
Now let's also see a method which doesn't need an escaping closure, that'll be the body parameter of Sequence's forEach(_:) method: this method executes the given closure on every element of the sequence, that'll be as in doing:
func forEach(_ body: (Element) -> Void) {
for element in self {
body(element)
}
// the function returns only after the for-in loop has completed,
// hence body closure only executes in the scope of this method,
// thus it never escapes.
}
(for the sake of simplicity I've omitted the throwing annotations)
In this case the body closure never escapes forEach(_:) method's scope, therefore it doesn't need to be annotated as #escaping: it will only executes inside the for-in loop which has to complete before the method can return.

Completion handler being called on background Thread instead of main UI thread in iOS

I have a networking class that does my fetching of data from the server. In the completion handler of that class, it looks something like this:
func fetchData(url: URL, completion: #escaping (Result<Data, MyError>) -> Void) {
let request = URLRequest(url: url)
fetch(request: request) { (result: Result<Data, MyError>) in
switch result {
case .success(let response):
DispatchQueue.main.async {
completion(.success(response))
}
case .failure(let error):
DispatchQueue.main.async {
completion(.failure(error))
}
}
}
}
If I call this fetchData method from my ViewController, I get the callback on the main thread and I don't have to reload my collection view on the main thread. I then tried adding a view model for my view controller. So the flow looks more like:
ViewController -> ViewModel (fetchData) -> Networking (fetchData)
where basically each class just calls a method that looks exactly like the above fetchData method, passing the completion upwards. In ViewController, do I need to check again that I'm on the main thread. Could iOS switch threads during these calls? I ask because I did get a warning about updating the UI was not called on the main thread one time. But I'm not sure if that was a false negative from this call since I have other networking calls to fetch images, and maybe I messed something else up elsewhere. But basically, I'm just asking if I don't do any other GCD type tasks, but only use completion handlers and bubble up the completion from the single networking call that calls back on the main thread, do I need to check again somewhere up the chain (like in the ViewController).
You haven't provided the code fo "these calls", so it isn't possible to say whether code will be dispatched on another queue, however, the system doesn't arbitrarily switch to another queue while executing code. You need to explicitly or implicitly dispatch onto another queue. Your code above contains an explicit dispatch onto the main queue and an implicit dispatch onto another queue when you call fetch (Somewhere in that code will be an implicit dispatch onto another queue, perhaps in code where you can't see the source).
As a simple answer to your question, if you dispatch onto the main queue in the completion handler shown and none of the other code called "further up" performs asynchronous work or explicitly dispatches onto a queue other than the main queue you can be certain that execution will continue on the main queue.
Also, you can simplify your code by simply calling the upstream completion handler directly:
func fetchData(url: URL, completion: #escaping (Result<Data, MyError>) -> Void) {
let request = URLRequest(url: url)
fetch(request: request) { (result: Result<Data, MyError>) in
DispatchQueue.main.async {
completion(response))
}
}
}
When designing your code you should adopt one of two approaches and stick to it:
Dispatch onto the main queue early. This approach is often taken by frameworks that may well be consumed by someone else; For example AFNetworking explicitly documents that completion handlers are dispatched onto the main queue so you don't need to worry about it. The disadvantage of this approach is that programmers may not read the documentation and may dispatch onto the main queue defensively, leading to double asynchronous dispatch or they may not be updating the UI and don't need main thread execution. This is an overhead but unlikely to be a major issue.
Never dispatch onto the main queue and rely on the calling code to dispatch if it needs to do so. This approach may be more common where all of the code is part of one solution and the programmer "knows" that they ultimately need to dispatch onto the main queue. The advantage of this approach is that you defer (and potentially avoid entirely if it isn't required) dispatching work to the main queue. The disadvantage is that if you forget to do it you will get warnings and main thread violations
if you're talking about this:
func fetchData(url: URL) { result in
print(result) // <-- This on main thread and should not cause any warnings
}
If you're certain that's what's happening then it's a false positive. But I highly doubt it. I've never seen it malfunction. You can easily use the Main Thread Checker and detect mistakes.
Aside from that normally functions shouldn't dictate the completionHandler's thread. ie it's on the caller to dispatch the thread. I mean if you ever wanted to dispatch this to another thread, then you'd be dispatching it twice which isn't ideal.

Multiple Completion blocks

I was following this tutorial:
https://medium.com/swift2go/building-grpc-client-ios-swift-note-taking-app-6133c7d74644
But I dont understand this piece of code as it got multiple completion handlers and I dont understand how this code works (I know this is part of a singleton class but what this code is doing and what is "notes?.notes" ???:
func listNotes(completion: #escaping([Note]?, CallResult?) -> Void) {
_ = try? client.list(Empty(), completion: { (notes, result) in
DispatchQueue.main.async {
completion(notes?.notes, result)
}
})
}
Im stuck at this point from 8 days straight so please help me :(
listNotes(_:) has its own completion block named completion.
In the body of the listNotes(_:)'s completion, listNotes(_:) calls an asynchronous, throwing function on the client variable named list(_:, _:).
The function list(_:, _:) has its own completion block and passes two variables into it; notes and result.
When list(_:, _:)'s completion block executes, the first thing that happens is it creates a GCD block to execute listNotes(_:)'s completion on the main thread.
Then it passes two block variables from list(_:, _:)'s closure forward into listNotes(_:)'s closure; a property on the optional variable notes also named notes (should be refactored imo) and result.
One very important thing to note here is that because client.list(_:, _:) is a throwing function and the error is never caught, if that function does throw, listNotes(_:) will never execute its completion block. I generally consider this to be very bad practice since someone could depend on that function to execute its closure regardless of success. You're essentially setting yourself up to break a promise you made in the function signature.

Why we need the synchronous operation in ios

I want to know As we all know how asynchronous task are necessary for concurrency but Wanted to know why we need the synchronous tasks. while we can achieve the same with the normal usage of function.
Thanks & regards
Rohit
When you calls something synchronously, it means that 'the thread that initiated that operation will wait for the task to finish before
continuing'. Asynchronous means that it will not wait for finish the task.
synchronous calls stops your current action and returns when the call returned. with asynchronous calls you can continue.
synchronous is the opposite of asynchronous code, and therefore is ordinary code.
At the end, if asynchronous is totally out of scope then you will not emphasize the word synchronous.
It helps to synchronise threads, as the name suggests.
consider a typical usage of GCD async and sync (pseudo)
async background_thread {
//1 call webservice or other long task that would block the main thread
sync main_thread {
//2 update UI with results from 1
}
//3 do something else that relies on 2
}
now if 2 was in an async and you needed to do something at 3 that relies on the updates at 2 to have happened, then you are not guaranteed (and most likely wont) get the behaviour you are expecting. instead, you use a sync to make sure that the task is completed before continuing the execution in the background thread.
If you are asking now, why not just take out the sync/async around 2 so it executes in order anyway? the problem is, the UI must not be updated on a background thread otherwise the behaviour is undefined (which usually means the UI lags a lot). So in essence what happens is the background thread waits at 2's sync until the main thread gets round to executing that block, then it will continue with the rest of the execution on the background thread.
If you were dealing with a task that doesnt require the main thread (or some other thread) to execute properly, then yes you may as well take out the sync at 2.
This is just one example of how a sync is useful, there are others if you are doing advanced threading in your app.
Hope this helps
Typically it's because you want to do an operation on a specific different thread but you need the result of that operation. You cannot do the operation asynchronously because your code will proceed before the operation on the other thread completes.
Apple has a very nice example:
func asset() -> AVAsset? {
var theAsset : AVAsset!
self.assetQueue.sync {
theAsset = self.getAssetInternal().copy() as! AVAsset
}
return theAsset
}
Any thread might call the asset method; but to avoid problems with shared data, we require that only functions that are executed from a particular queue (self.assetQueue) may touch an AVAsset, so when we call getAssetInternal we do it on self.assetQueue. But we also need the result returned by our call to getAssetInternal; hence the call to sync rather than async.

Swift + Async: How to execute a callback on the same thread where it was created?

Using the Async library, a simple pattern to do work on a background thread might look like this:
// Assume we start on the main thread
let onResultComplete: (result: ResultType) -> Void = { result in
Async.main {
// Code to handle one result at a time on the main thread
}
}
Async.background {
doCalculationsThatProduceManyResults(onEachResultComplete: onResultComplete)
}
Now consider this scenario, where the code is already being executed on a background thread:
// Assume we start on some "unknown background thread"
let onResultComplete: (result: ResultType) -> Void = { result in
Async.??? {
// Code to handle one result at a time on the "unknown background thread"
}
}
Async.background {
doCalculationsThatProduceManyResults(onEachResultComplete: onResultComplete)
}
How can I force the closure onResultComplete to be run on the same unknown background thread from where I called Async.background?
I'm open to any suggestions that use GCD methods.
On iOS or macOS, if some code executes on an unknown thread or dispatch queue (say: "execution context"), there's no means to reliable obtain some "handle" for it - well, unless this is the main thread.
So, the solution to your problem is to first create or obtain a known execution context (aka dispatch queue or thread) and execute your code here. Then, in the continuation (aka completion handler), explicitly dispatch back to this same execution context again and continue with your code.
Don't call Async.anything. Simply run the code in-line (assuming the Async library calls it's completion block on the same thread where the calculations are run.
Your question is specific to the Async library, so you should put that in your title and add a tag for it. (I've never used it, so I don't know the specifics of how it works.)

Resources