Multiple Completion blocks - ios

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.

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.

Is asynchronous code always run on a different thread?

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.

Variable becomes nil outside completion handler [duplicate]

This question already has an answer here:
Swift || Returning a class that can be used by other methods from an API call
(1 answer)
Closed 3 years ago.
I am not sure if this is a duplicate or not, I searched and couldn't find an answer. If it is please point me to the similar question.
I am setting the value of the imageURL variable inside a completion handler:
var imageURL: URL?
let options = PHContentEditingInputRequestOptions()
imageAsset.requestContentEditingInput(with: options, completionHandler: { (contentEditingInput: PHContentEditingInput?, _) in
imageURL = contentEditingInput?.fullSizeImageURL
})
//prints nil
print(imageURL)
Now, the value of imageURL inside the handler is not nil, but it becomes nil right after (in the print statement). Why is this happening and how can I fix it without having to write all of my subsequent code inside the completion handler block?
You can't "fix" it in the way you'd like, unfortunately. The completion handler here might in theory be called synchronously (ie, at the time of the call to requestContentEditingInput), but can (and most likely will) be called at some time later, when the asset is ready. That could include actual downloading or any other unpredictable time-consuming preparation of the asset that happens on some other thread.
In other words, the function requestContentEditingInput returns to you right away (and your code continues executing), but the function also commences doing some work in the background. That background work, when it finishes, calls your handler block.
The nature of execution flow means that you simply cannot guarantee (and certainly cannot assume) that the handler will be called before execution moves on to your print(imageURL) line.
This kind of asynchronicity is a very common pattern, though! Nothing to be afraid of. You need to put any code that must run subsequently within that handler block (or call out from the handler block to another function if that is cleaner within your file).
It is likely that the completion is not called until later because the code is running on another thread. Try putting a print statement inside of the completion block to see what order the code is executed.
When you work with handlers, the time with the threads can be different every build.
I recommend you create a method that will be called inside the handler.
Like this:
func edit(){
var imageURL: URL?
let options = PHContentEditingInputRequestOptions()
imageAsset.requestContentEditingInput(with: options, completionHandler: { (contentEditingInput: PHContentEditingInput?, _) in
imageURL = contentEditingInput?.fullSizeImageURL
display(url:imageURL)
})
}
func display(url: String) {
print(imageURL)
}

What happens if my DispatchGroup never finishes

I have many asynchronous tasks that need to all be completed before I can move on, so I'm using a DispatchGroup. However, I am able to tell if one of the tasks fails early, and I would usually call a completion handler.
If I leave the scope of the function by calling a completion handler, what happens to the DispatchGroup?
Is the memory allocated forever? Is it possible that if leave is called enough times eventually (maybe due to a bug) that the notify block can still be called?
Example:
func example(completion: #escaping (Bool) - Void) {
let group = DispatchGroup()
group.enter()
asyncFunction1 {
if result == false {
completion(false)
} else {
group.leave()
}
}
group.enter()
asyncFunction2 { result in
if result == true {
group.leave()
}
}
group.notify(queue: .main) {
completion(true)
}
}
In the example above I have two asynchronous functions. The first function might call completion(false), and the second function only calls leave on success. While it might not be the best example of code, these conditions are possible. What happens to the notify block?
From the swift's source on GCD:
https://github.com/apple/swift-corelibs-libdispatch/blob/master/src/semaphore.c
It seems the block will be allocated and never released. Meaning, the memory the block retains will also never be released.
The dispatchgroup must be notified whenever a code block enters the group as well as whenever a codeblock leaves the group.
That means, dispatchGroup.leave() must be called regardless of success or failure of your function.
When all blocks are finished, the dispatchgroup gets notified.
I'm wondering what happens when leave is never called. Just because it must be called, doesn't mean it always will be called.
Well, don't "never call" it. You must arrange your code so that leave is called by every possible exit path. If you can't, don't use dispatch groups.
If necessary, you can pass the dispatch group itself into the completion handler so that it can call leave for you. But one way or another, call leave.

Does return need to be called after an escaping completion block?

If I have the following code:
func getData(completion: #escaping () -> ()) {
// Do stuff
completion()
print(hello)
}
When the completion block is called, will the function automatically return, or will it continue running and "hello" be printed?
Completion handlers are just like any other ordinary closure. In some cases people might want to run some more code after completion, so that's why they designed them like so. When you call them, they run. And after they return, the code below runs. They cannot replace a return statement. If you want to stop running the rest of the method after the completion() bit, put a return there.
Unless, your completion handler is declared to return Never, in which case it will either terminate your whole app, or run indefinitely. In that case any code after it won't be run. :)

Resources