Return Callback inside Callback Swift - swift

I have a SDK integration that returns a response using a completion but I want to create another completion to return the callback response, but I don't know how.
This is my attempt to do that
func validatingProcces(completion: ((Any)?)->()) {
let conekta = Conekta()
conekta.delegate = self
conekta.publicKey = "key_KJysdbf6PotS2ut2"
conekta.collectDevice()
let card = conekta.card()
let token = conekta.token()
card!.setNumber(String(cardToSave!.cardNumber), name: cardToSave!.fullName, cvc: String(cardToSave!.cvc), expMonth: String(cardToSave!.month), expYear: String(cardToSave!.year))
token!.card = card
token!.create(success: { (data) -> Void in
completion(data as Any)
}, andError: { (error) -> Void in
print(error as Any)
completion(error as Any)
})
}
I have the following error:
Escaping closure captures non-escaping parameter 'completion'
and also:
Parameter 'completion' is implicitly non-escaping
Captured here
Ps. You'll find the SDK integration here:
https://github.com/conekta/conekta-ios
Thank you so much!

From the source code it looks like you could just make a callback like this:
completion: #escaping (Any?, Error?) -> ()
and pass in the result of the api callback so you can handle it elsewhere like this
token!.create(success: { data in
completion(data, nil)
}, andError: { error in
print(error as Any)
completion(nil, error)
})
Let me know if this works

Related

Does a Completion Handler end the function?

Perhaps I do not understand the concept of a completion handler, but I have a function like so;
func someFunction(completion: #escaping (Bool, LoginError?) -> Void) {
self.checkDevice() { allowed, error in
if let e = error {
completion(false, e)
}
completion(true, nil)
}
}
While being light on what checkDevice() does, the basic premise is that it performs an asynchronous network call, and returns either true with no error (nil), or returns false with an error.
When I run this code, I am finding that the completion handler is being called twice. It sends a completion as a tuple (as false, error) and also as (true, nil). I've done some debugging and there seems to be no manner in which someFunction() is called twice.
I was of the belief that once a completion is sent, the function would end. In my test case, I am forcing an error from checkDevice(), which should result in me sending the completion as (false, error), but I am seeing both (false, error) and (true, nil). Does a completion not immediately end the function?
I was of the belief that once a completion is sent, the function would end.
No, why would that be? When you call this function it’s like calling any other function. The name has no magic power.
Rewrite it like this:
func someFunction(completion: #escaping (Bool, LoginError?) -> Void) {
self.checkDevice() { allowed, error in
if let e = error {
completion(false, e)
return // *
}
completion(true, nil)
}
}
Actually, I should mention that this is a poor way to write your completion handler. Instead of taking two parameters, a Bool and an Optional LoginError to be used only if the Bool is false, it should take one parameter — a Result, which carries both whether we succeeded or failed and, if we failed, what the error was:
func someFunction(completion: #escaping (Result<Void, Error>) -> Void) {
self.checkDevice() { allowed, error in
completion( Result {
if let e = error { throw e }
})
}
}
As you can see, using a Result as your parameter allows you to respond much more elegantly to what happened.
Indeed, checkDevice itself could pass a Result into its completion handler, and then things would be even more elegant (and simpler).
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
Consider including parameter names (for reference). And the completion should only be called once. You can do that using return or by using a full if-else conditional.
func someFunction(completion: #escaping (_ done: Bool, _ error: LoginError?) -> Void) {
checkDevice() { (allowed, error) in
if let e = error {
completion(false, e)
} else {
completion(true, nil)
}
}
}
func someFunction(completion: #escaping (_ done: Bool, _ error: LoginError?) -> Void) {
checkDevice() { (allowed, error) in
if let e = error {
completion(false, e)
return
}
completion(true, nil)
}
}
By giving parameters names, when calling the function, you can now reference the signature to denote the meanings of its parameters:
someFunction { (done, error) in
if let error = error {
...
} else if done {
...
}
}

Swift error while returning multiple values from function

I can not understand what I did wrong
I have an app which loads posts and its comments. The view controller requests a function from an another file, which returns back (response?, comments?)
I get one error:
Initializer for conditional binding must have Optional type, not '(ActionResult?, [PostComment]?)'
For the line
if let (response, comments) = (response, comments )
What did I wrong?
commentsViewController
postComments.loadCommentForPost(id: postId) { (response, comments) in
// ERROR here: Initializer for conditional binding must have Optional type, not '(ActionResult?, [WorldMessageComment]?)'
if let (response, comments) = (response, comments ) {
if response!.success == 1 {
DispatchQueue.main.async(execute: {() -> Void in
self.comments = comments!
self.tableView.reloadData()
})
} else {
DispatchQueue.main.async(execute: {() -> Void in
self.handleResponses.displayError(title: response!.title, message: response!.message)
})
}
}
}
commentFunctions
func loadCommentsForPost(id: Int, completion: #escaping ((ActionResult?), [PostComment]?)->()){
// downloading the data and then
let comments : [PostComment] = ...
// Succesful
return completion((ActionResult(success: 1, title: responseTitle, message: responseMessage)), comments)
}
The issue is in the line:
if let (response, comments) = (response, comments ) {
What you are basically doing is creating a new non optional tuple on the right hand side of the assignment (with two optional components), so the compilator complains that you can't use if let with a non optional type.
You can in fact consider that the tuple return by loadCommentsForPost is already "split" in the arguments of the callback, so you can handle response and comments separately.
postComments.loadCommentForPost(id: postId) { response, comments in
if let response = response, let comments = comments {
if response.success == 1 {
...

Swift completion handler - escaping trailing closure

I've been reading quite a few articles now about swift functions with closures, trailing closures and escaping functions. They all seem to give examples which is sufficiently different that I'm not understanding what Im doing wrong with my own function.
My main problem is with how to execute a trailing closure function.
I've created this function to upload an image to firebase. It takes two inputs and are supposed to return a string (imageURL). I do belive this function is ok.
func uploadImageToFirebaseAndReturnImageURL(directory: String, image: UIImage!, handler: #escaping(_ imageURL: (ImageURL)) -> ()) {
let imageName = NSUUID().uuidString // create unique image name
if let uploadData = UIImagePNGRepresentation(image) {
DB_STORE.child(directory).putData(uploadData, metadata: nil, completion: { (metadata, error) in
if error != nil {
print(error)
return
}
if let profileImageUrl = metadata?.downloadURL()?.absoluteString {
let d = ImageURL(imageURL: profileImageUrl)
handler (d)
}
return
})
}
}
My issue is how to execute this function correctly when it comes to the handler.
I want to first execute the function then when complete I want to get the imageURL and use this variable into another nested function that upload this variable(String) into a firebase database.
uploadImageToFirebaseAndReturnImageURL(directory: "profileImage", image: selectedImageFromPicker!, handler: { imageURL in
guard let uid = Auth.auth().currentUser.uid else { print("User is not logged in"); return }
DataService.instance.updateUserWithProfileImageURL(uid: uid, imageURL: imageURL)
print("")
}
What am I doing wrong?
To pass a trailing closure you need to end/close your function call and omit the closure argument label. For instance:
func foo(first: Int, second: Int, handler: (Int) -> Void) {
...
}
call syntax:
foo(first: 10, second: 20) { result in
/* trailing closure body */
}
By the way, you should simply your handler argument declaration from:
handler: #escaping (_ imageURL: (ImageURL)) -> ()
to this:
handler: #escaping (ImageURL) -> Void
Using Void or () is matter of style since they are logically the same. I prefer the former ;)

Chaining closures and completion handlers Swift 3

I'm having a hard time understanding how chaining completion handlers with closures works.
The example I'm working with is the following :
typealias CompletionHandler = (_ result: AnyObject?, _ error: NSError?) -> Void
func getRequestToken(completionHandler: CompletionHandler){
taskForGET(path: "PathToWebsite.com") { (result, error) in
if let error = error {
print(error)
} else {
print(result)
}
}
}
func taskForGET(path: String, completionHandler: CompletionHandler) {
//URLSESSIONCODE with completion handler
(data, response, error) {
if let error = error {
CompletionHandler(result: nil, error: error)
} else {
let data = data
parseJSON(data: data, completionHandler: completionHandler)
}
}
}
func parseJSON(data: NSData, completionHandler: CompletionHandler) {
//SerializingJSON with do catch block
do {
completionHandler(result: theResult, error: nil)
} catch {
completionHandler(result: nil, error: error)
}
}
So basically what this code does is it kicks off a GET request to a server. If the GET request sends back data, then it parses it into JSON. If at any point along the way something fails, it returns an error.
I understand basically what is going on here, but I don't understand how the completion handlers are being fired off.
First taskForGET is called which has a completion handler as a parameter that can return a result or an error, I've got that.
The next step is calling parseJSON, where the data from taskForGET is passed but then the completionhandler that's being passed is taskForGET's completion handler. I don't understand this at all.
Then down in parseJSON, the completion handler either returns JSON or an error by calling the completion handler from its parameters..which in this case is taskForGET's completion handler?
I don't understand the flow. Even once we've parsed JSON successfully, how does calling taskForGET's result ever get back up to getRequestToken.
Any help with this would be appreciated.
There is only one completion handler which is passed from one method to another.
Lets declare the handler separately, btw. in Swift 3 omit the parameter labels in the type alias:
typealias CompletionHandler = (Any?, Error?) -> Void
let handler : CompletionHandler = { (result, error) in
if let error = error {
print(error)
} else {
print(result)
}
}
This closure is supposed to be executed at the end of the process. Now the getRequestToken method looks like
func getRequestToken(completionHandler: CompletionHandler){
taskForGET(path: "PathToWebsite.com", completionHandler: handler)
}
The handler / closure is passed as a parameter from getRequestToken to taskForGET and then from taskForGET to parseJSON but it's always the same object.

Calling a function that has a TypeAlias as a parameter?

So I've written a little practice program that has to do with closures. I'm trying to better understand how the asynchronous concept works. When I try to call request(), I get conversion errors as seen below:
import UIKit
let correctPasscode = "3EyX"
typealias CompletionHandler = (result: AnyObject?, error: String?) -> Void
func request(passcode: String, completionHandler: CompletionHandler) {
sendBackRequest(passcode) {(result, error) -> Void in
if error != nil {
print(error)
}
else {
print(result)
}}
}
func sendBackRequest(passCode: String, completionHandler: CompletionHandler) {
if passCode == correctPasscode {
completionHandler(result: "Correct. Please proceed", error: nil)
} else {
completionHandler(result: nil, error: "There was an error signing in")
}
}
request(correctPasscode, completionHandler: CompletionHandler) // Error happens here
Type alias is there to tell you what actual type you need to pass. In this case, the type is a closure of type
(result: AnyObject?, error: String?) -> Void
You pass it like this:
request(correctPasscode, completionHandler:{
(result: AnyObject?, error: String?) in
print("Inside the handler...")
// Do some useful things here
})
or even shorter -
request(correctPasscode) {
(result: AnyObject?, error: String?) in
print("Inside the handler...")
// Do some useful things here
}
or even shorter - (the types are known via the func declaration) -
request(correctPasscode) { result, error in
print("Inside the handler...")
// Do some useful things here
}
I'm not sure what you try to accomplish, but this line:
request(correctPasscode, completionHandler: CompletionHandler)
doesn't compile because you are not passing a CompletionHandler closure to the function but rather a type object representing the type of that completion handler.
Hence the error: Cannot convert value of 'CompletionHandler.*Type*'.
A valid call would be:
request(correctPasscode) { result, error in
print("result was \(result), and error was \(error)")
}
But then your request function doesn't do anything with the closure which is passed in. It is a little hard to say what you want ...