Why Does the Completion Handler Does Not Return Anything

Does a Completion Handler end the function?

A completion handler does not end a function. The typical way to end a function is to insert a return.

See comment re: your specific case

completion handler not returning anything even though its looks like it is when stepping through it

You have a few issues.

  1. Inside the completion block of the data task, you are calling strongSelf.onCompletion?... but onCompletion is nil so it doesn't do anything.
  2. Your getNetworkConnection function has a completionHandler parameter that you never use.
  3. Your call to getNetworkConnection is depending on the completionHandler being called.

There seems to be no point to the onCompletion property.

Inside the data task, replace calls to strongSelf.onCompletion?... with calls to the completionHandler parameter.

typealias ResultType = Result<Data>
typealias CompletionType = (ResultType) -> ()

internal func getNetworkConnection(urlRequest: URLRequest, completionHandler: @escaping CompletionType) {
let task = self.urlSession.dataTask(with: urlRequest, completionHandler: { [weak self] (data, response, error) in
guard let strongSelf = self else {
return
}

if let errorResponse = error {
completionHandler(Result.failure(errorResponse))
}

if let responseFromServer = response as? HTTPURLResponse,
200...299 ~= responseFromServer.statusCode{

if let dataResponse = data{
completionHandler(Result.success(dataResponse))
} else {
completionHandler(Result.failure(NSError()))
}
} else {
// TODO - need to call completionHandler here too
}

strongSelf.urlSession.invalidateAndCancel()
})

task.resume()
}

Why does my completion handler does not return the needed value?

As i can see you dont call your completion() anywhere
To fix that issue try to user completion(your responce) to pass data via your completion

Completion Handler not working Properly in swift

You are actually not escaping the closure.
For what I know the "@escaping" is a tag that the developper of a function use to signify the person using the function that the closure he/she is passing will be stored and call later (after the function ends) for asynchronicity and memory management. In your case you call the closure passed immediately in the function itself. Hence the closure is not escaping.

Also the firebase database is asynchronous. Meaning that you don't receive the result immediately

This part :

{ (snapshot, error) in
if error == nil{
for document in snapshot!.documents {
let documentData = document.data()

//print(document.documentID)
//print(documentData)

self.countries.append(document.documentID)
}

}
}

is itself a closure, that will be executed later when the result of the query is produced. As you can see in the doc, the function is escaping the closure : https://firebase.google.com/docs/reference/swift/firebasefirestore/api/reference/Classes/Query.html#getdocumentssource:completion:

func getDocuments(source: FirestoreSource, completion: @escaping FIRQuerySnapshotBlock)

So to summarise :
The code for the firebase query will be call later (but you don't know when), and your closure "finished" is called immediately after having define the firebase callback, thus before it has been called.

You should call your finished closure inside the firebase callback to have it when the arrays are populated.

What are the effects of never calling a function's completion handler?

Fundamentally, there's nothing special about a "completion handler". It's just a function.

If you have a function:

func f() {
print("Hello, world")
}

and you don't call it, what happens then? Well, nothing happens.

Now, what are the implications of that? As rmaddy mentioned, if a function has a completion, the invocation of that completion is certainly expected by callers. It's unusual at best for a function to take a completion and the caller to not care whether the completion is ever run.*

Imagine the function is called by a view controller that starts an activity indicator before it calls, and then in the completion it stops the indicator. If the completion is never called, then the indicator never stops spinning.

Fundamentally, that's it. If the completion isn't called, its code doesn't run. The effects of that are completely dependent on what's going on in the code in and around the completion.


Okay, you mentioned memory... A lot of the time, a completion handler will be an anonymous function. In that case, there is an object created. But it's subject to the same memory management rules as any other object. There is no leak just because it is not called. If completeMe looks like this:

func completeMe(_ completionHandler: (String) -> Void) {
completionHandler("Hello, world")
}

and you call it like this:

completeMe({ self.detailLabel.text = $0 })

Then the object created for the anonymous function doesn't live past when completeMe returns. If you store the anonymous function:

func completeMe(_ completionHandler: @escaping (String) -> Void) {
self.handler = completionHandler
// Do other things
}

Then the same thing happens as with any other object: the anonymous function lives until that property is set to another value (given also that nothing else has a reference to it).

And if you pass a named function in,

func takeAString(_ s: String) {
print(s)
}

completeMe(takeAString)

the lifetime of takeAString is already the lifetime of the program; there's no memory management implications here.

If you pass an instance method in, the memory management is the same as if you had passed the instance itself in.


*Generally if failure is possible you would either a) have a second "failure" completion or b) the completion would signal failure via its argument.

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

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. :)



Related Topics



Leave a reply



Submit