This question already has answers here:
swift : Closure declaration as like block declaration
(2 answers)
Closed 5 years ago.
Can someone explain what completionHandler: ((Bool) -> Void) means?
For instance it appears when requesting camera access:
AVCaptureDevice.requestAccess(for: AVMediaType.depthData, completionHandler: (<#T##(Bool) -> Void#>))
I usually do this to check if access was granted or not:
func requestCamera() {
AVCaptureDevice.requestAccess(for: AVMediaType.video) { (response) in
if response {
print("true")
} else {
print("denied")
}
}
}
Obviously I do stuff there, but that doesn't matter here. I just want to understand what ((Bool) -> Void) means and why I have to use the completion handler here. With other functions I can just set the handler to nil, but in this case it expects a response in some way.
So what does this mean?
Read Closures section of the Swift documentation.
It is a completion closure that gets executed when the AVCaptureDevice.requestAccess is finished with requesting access from the user. It has one Bool parameter that is true/false according to whether the user granted the access or not. It is not optional, so you have to provide some closure - that was a decision of the AVCaptureDevice.requestAccess author, and it makes sense - if you are requesting the access, you are requesting it because you want to use AVCaptureDevice. Therefore the author expects you to react to completing the requestAccess in some way.
Closure expression syntax has the following general form:
{ (parameters) -> return type in
statements
}
The parameters in closure expression syntax can be in-out parameters, but they can’t have a default value. Variadic parameters can be used if you name the variadic parameter. Tuples can also be used as parameter types and return types.
completionHandler: ((Bool) -> Void) this means that you'll get a Boolean value in your closure and it will return nothing(Void). Just like a method.
You can find this more about in Apple's documentation about closures
(Bool) -> Void means a closure take accepts a Bool as a argument and return a Void (i.e. nothing).
You provide a completion handler because requestAccess execute asynchronously. When the user has decided what permission to give your app, iOS will call the completion handler to continue execution of your program.
The majority of closures are used to handle some information that will be available later, for instance, a back end request that will take some seconds to respond, or even some action is required before some operation.
When you call a function that has a completion handler, it's the same as asking for something in the future, that will execute when the information is available, it's a great solution for asynchronous stuff or when you need to wait for some action, and then continue when you got the necessary data.
Related
I'm creating shortcuts for a communication app. In this app there is a presence state (available, dnd, ...). I implemented an intent to set said presence state which worked fine. I created a "read" intent which just returns the currently set state, which works, but i can't pass the result to the next action.
For simplification and testing i created another intent just return a string, trying to process it, but i'm still unable to do that. My intent has no input parameters.
What am i missing? My Goal would be an interaction like Get Current Location offers.
class Return5IntentHandler: NSObject, Return5IntentHandling {
func handle(intent: Return5Intent, completion: #escaping (Return5IntentResponse) -> Void) {
completion(.success(result: "5"))
}
}
Classic case of searching for hours, writing a question only to find the answer 30min later yourself...
One has to explicitly set the output, which is the passed on value for following actions.
I am using Google Sign-In 5.0.2 with a Swift 4 iOS app. Here is my code to get a current id Token:
public static func getJwtToken(completion: #escaping (Result<String, Error>) -> Void) {
assert(Config.isAuthEnabled())
GIDSignIn.sharedInstance()?.currentUser?.authentication.getTokensWithHandler({ gidAuth, error in
if let error = error {
completion(.failure(error))
} else if let token = gidAuth?.idToken {
completion(.success(token))
} else {
assertionFailure("shouldn't have come here")
}
})
}
This works fine when I launch the app.
But if I leave the app running for an hour (to let the id token expire), then the next time getTokensWithHandler is called, it will not call my closure. Subsequent calls to getTokensWithHandler will once again call my closure.
I would like it to behave consistently so that my closure is always called, even if the token needs to be refreshed.
Anyone have any ideas on what I need to do to achieve this?
I figured out the answer to this problem after spending some more time on it.
My problem was I was trying to hack the Google SDK's closure to be synchronous instead of just letting it call the closure when it was ready. The function call that I claimed never called its closure would've called it if I had let the program flow naturally instead of trying to make it call the closure when I expected it to be called. I was using a semaphore somewhere in the call stack that was waiting until the closure got called and I just needed stop making assumptions about when Google's SDK would perform its work.
I'm still trying to get used to this async-first paradigm of Swift.
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.
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)
}
There are questions with the similar to this one, what I have learned is that, completion handler using call back function, blocks are just anonymous functions or closures
Example of block and completion handler
class func scheduledTimer(withTimeInterval interval: TimeInterval,
repeats: Bool,
block: #escaping (Timer) -> Void) -> Timer
func startUpdates(from start: Date,
withHandler handler: #escaping CMPedometerHandler)
Question1
I've noticed that another difference is the completion handler has the typealias for the type, Does this applies to all ios framework design?
typealias CMPedometerHandler = (CMPedometerData?, Error?) -> Void
Question2
Does blocks also use call back function? Blocks are also has the escaping keywords, which means the closure will be called after the outer function has been returned, this sounds very "call back" to me.
Question3
If the answer for question 2 is yes, then what's the reason to reinvent the wheels, why not just call them all blocks or completion handlers
You are talking about terms which actually mean the same thing. Therefore your 3 questions cannot be answered separately.
Both completion handler and callback are synonyms for an (escaping) closure – which is the Swift name for a block.
#escaping indicates that the closure is called later after the enclosing function returns.
A typealias is just a convenience identifier to replace the more complex right side with a simpler left side. It's not directly related to closures.