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 theopen
function and then callself.callback()
in thecontactPicker
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
Hashtags in Arabic Language Crashes the App
Uilongpressgesturerecognizer Not Calling Its Target Method
Why Force Unwrapping Is Required in Case of Enum and Switch
Swift 3: Unrecognized Selector Sent to Instance Xcode 8
Accessing Core Data Stack in Mvvm Application
Converting Int to Float Loses Precision for Large Numbers in Swift
Uicolor Colorwithred:Green:Blue:Alpha: Always Returns White Unless One Argument Is 0
Increment Uislider by 1 in Range 1 to 100
How to Broadcast Multiple Ibeacon Signals from Only One Bluetooth? and How
Best Way to Use Background Location Updates in iOS (Swift)
Fix Cordova Geolocation Ask for Location Message
How to Get Index Path of Cell on Switch Change Event in Section Based Table View
How to Set Cornerradius for Only Bottom-Left,Bottom-Right and Top-Left Corner of a Uiview