Returning Object from Callback in Swift

Returning object from callback in Swift

You can never return a placemark from your placemarkForSearchResult function, because obtaining a placemark requires the use of an asynchronous function (geocodeAddressString). By that time that function has finished and calls back into your completion handler, your placemarkForSearchResult has already finished and returned long ago!

You need a strategy compatible with the asynchronous nature of the function you are calling. Instead of returning a placemark from placemarkForSearchResult, you need placemarkForSearchResult to accept a callback function parameter. When you have your placemark in the completion handler, you call that callback function. If that callback function is (cleverly) designed to accept a placemark parameter, you are now handing that placemark to whoever called you in the first place.

Callback function in Swift to return value during JSON task

Assuming that you're getting proper result in calculateRate method, call the method calculateRate(value:completionHandler:) like so,

var arrayResults = [String:Any]()

calculateRate(value: ["a", "b", "c"]) {(results) in
arrayResults = results
}

completionHandler is a closure. So, when calling calculateRate, you need to pass a closure there accepts a parameter results of type [String:Any].

Edit:

Also add @escaping with completionHandler in the calculateRate(value:completionHandler:) method's signature.

func calculateRate(value: [String], completionHandler: @escaping (_ results: [String: Any])->()){
//rest of the code
}

Swift - How to return json object from an API call in Model to use in View Controller

The errors are actually self-explanatory. Use of unresolved id means that the method you are calling is not in the current scope. Since getContactUsApiCall is actually declared inside the class ContactUsModelClass, you'll need to call it from an object of that class. Alternatively, you could modify the method to be a class method and call it as ContactUsModelClass.getContactUsApiCall(...).

As for the second error, response.result.value is of the type GetContactusResponse?. Hence, your callback's signature should also use that type. Like so:

class func getContactUsApiCall(URL: URL, callback: @escaping ((GetContactusResponse) -> ())) {
Alamofire.request(URL, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: nil)
.responseObject { (response: DataResponse<GetContactusResponse>) in
switch response.result {
case .success:
// optional is NOT NULL, neither NIL nor NSNull
guard let end = response.result.value else {
return
}
callback(end)
break
case .failure:
if let error = response.result.error as? URLError {
print("URLError occurred: \(error)")
} else {
print("Unknown error: \(String(describing: response.result.error))")
}
break
}
}
}

Callback function syntax in Swift

Rob's answer is correct, though I'd like to share an example of a simple working callback / completion handler, you can download an example project below and experiment with the getBoolValue's input.

Swift 5:

func getBoolValue(number : Int, completion: (Bool)->()) {
if number > 5 {
completion(true)
} else {
completion(false)
}
}

getBoolValue(number: 2) { (result) -> () in
// do stuff with the result
print(result)
}

Important to understand:

(String)->() // takes a String returns void
()->(String) // takes void returns a String

Return object for a method inside completion block

If you want the MakeGetRequest method to return data obtained via dataTaskWithURL, you can't. That method performs an asynchronous call, which is most likely completed after the MakeGetRequest has already returned - but more generally it cannot be know in a deterministic way.

Usually asynchronous operations are handled via closures - rather than your method returning the data, you pass a closure to it, accepting the parameters which are returned in your version of the code - from the closure invoked at completion of dataTaskWithURL, you call that completion handler closure, providing the proper parameters:

class func MakeGetRequest(urlString: String, completionHandler: (data: NSData, error: NSError) -> Void) -> Void
{
let url = NSURL(string: urlString)
var dataResponse: NSData
var err: NSError

let task = NSURLSession.sharedSession().dataTaskWithURL(url!, completionHandler: { (data, response, error) -> Void in
completionHandler(data: data, error: error)
})

task.resume()
}

Swift 5 update:

class func makeGetRequest(urlString: String, completionHandler: @escaping (Data?, Error?) -> Void) -> Void {
let url = URL(string: urlString)!
var dataResponse: Data
var err: NSError

let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, respone, error) -> Void in
completionHandler(data, error)
})

task.resume()
}

How to access callback function in adjacent Swift class method

maybe I could attach the callback to the parent class with a technique like self.callback = callback in the open function and then call self.callback() in the contactPicker itself

This is the right idea. You can declare a property to store the callback like this:

private var callback: RCTResponseSenderBlock?

@objc func open(_ options: NSDictionary, callback: @escaping RCTResponseSenderBlock) -> Void {
self.callback = callback
DispatchQueue.main.async {
self._presentContactPicker(options: options)
}
}

Note that the since the callback "escapes" into the class, you should mark the parameter as @escaping.

func contactPicker(_ picker: CNContactPickerViewController, didSelect contacts: [CNContact]) {
callback(someParameters)
}

iOS - Swift - Function that returns asynchronously retrieved value

Yes, it is possible to do this. Its called a closure, or more commonly a callback. A callback is essentially a function that you can use as an argument in another functions. The syntax of the argument is

functionName: (arg0, arg1, arg2, ...) -> ReturnType

ReturnType is usually Void. In your case, you could use

result: (image: UIImage?) -> Void

The syntax of calling a function with one callback in it is

function(arg0, arg1, arg2, ...){(callbackArguments) -> CallbackReturnType in
//code
}

And the syntax of calling a function with several callbacks is (indented to make it easier to read)

function(
arg0,
arg1,
arg2,
{(cb1Args) -> CB1Return in /*code*/},
{(cb2Args) -> CB2Return in /*code*/},
{(cb3Args) -> CB3Return in /*code*/}
)

If your callback function escapes the main function (the callback is called after the main function returns), you must add @escaping in front of the callback's argument type

You're going to want to use a single callback that will be called after the function returns and that contains UIImage? as the result.

So, your code could look something like this

func imageFromFile(file: PFFile, result: @escaping (image: UIImage?) -> Void){
var image: UIImage?

file.getDataInBackgroundWithBlock() { (data: NSData?, error: NSError?) -> Void in
//this should be 'error == nil' instead of 'error != nil'. We want
//to make sure that there is no error (error == nil) before creating
//the image
if error == nil {
image = UIImage(data: data!)
result(image: image)
}
else{
//callback nil so the app does not pause infinitely if
//the error != nil
result(image: nil)
}
}
}

And to call it, you could simply use

imageFromFile(myPFFile){(image: UIImage?) -> Void in
//use the image that was just retrieved
}


Related Topics



Leave a reply



Submit