How to return value from Alamofire
As mattt points out, Alamofire is returning data asynchronously via a “completion handler” pattern, so you must do the same. You cannot just return
the value immediately, but you instead want to change your method to not return anything, but instead use a completion handler closure pattern.
Nowadays, that might look like:
func getOrders(completionHandler: @escaping (Result<[String: Any]>) -> Void) {
performRequest("orders", completion: completionHandler)
}
func performRequest(_ section: String, completion: @escaping (Result<[String: Any]>) -> Void) {
let url = baseURL.appendingPathComponent(section)
let params = ["consumer_key": "key", "consumer_secret": "secret"]
Alamofire.request(url, parameters: params)
.authenticate(user: consumerKey, password: consumerSecret)
.responseJSON { response in
switch response.result {
case .success(let value as [String: Any]):
completion(.success(value))
case .failure(let error):
completion(.failure(error))
default:
fatalError("received non-dictionary JSON response")
}
}
}
Then, when you want to call it, you use this completion
closure parameter (in trailing closure, if you want):
api.getOrders { result in
switch result {
case .failure(let error):
print(error)
case .success(let value):
// use `value` here
}
}
// but don't try to use the `error` or `value`, as the above closure
// has not yet been called
//
How i can return value from function use Alamofire
Try to use a handler like this and a callback:
func getopt(callback:(array: [String]) -> void ){
func completion(request: NSURLRequest?, response:NSHTTPURLResponse?,result:Result<AnyObject>){
if let rdata = result.value{
let data = JSON(rdata)
print(data)
let myArray = [String]
let objects = data.array
for object in objects{
myArray.append(object)
}
callback(myArray)
}
}
let url = "http://www.xxxxxxxxxxxxx.com"
Alamofire.request(.GET,url),
encoding: .JSON).responseJSON(completionHandler: completion)
}
You pass the array to your callback So when you call getopt where you call it you can print the array. Some like this:
func something (){
getopt(callback)
}
func callback(array:[String]){
print array[0]
}
How to return value of array with Alamofire Swift 3?
You should use closures for this purpose:
func getPeople(success: (([Person])->Void)?, failure: ((Error)->Void)?) {
//create Alamofire request
//if everything was fine call success block with people array passed into it
//if failure occurred, call failure block with error.
//In any case you must call either `success` of `failure` here
}
Usage:
ServerManager.sharedInstance.getPeople(
success: { people in
//use people here, update UI if needed
},
failure: { error in
//show an error
})
You can read more about closures in swift here, but I will leave some explanation about your particular case:
Basically, (([Person])->Void)?
is a type of the closure. It means that the closure will accepts an array of Person
objects and is returning nothing (Void
). ?
in the end means that it's an optional parameter, so you can pass nil
when you call getPeople
function if you're not interested in the callback at all.
The same about ((Error)->Void)?
failure closure. If you call it, you must pass an Error
object to describe what actually went wrong.
Why you closures should be optional?
For example, you have an endpoint for sending user's location to the server. Sometimes you will want to run some code after request is finished and sometimes you don't care if it fails or not (you're just sending the coordinates in background). If you don't need a callback, you can pass an empty closure or nil. In fact there is nothing wrong in using empty closure like { _ in }
but it's a bit cleaner if you pass nil
. You can meet optional closures in some UIKit methods as well:
present(_:animated:completion:)
animate(withDuration:animations:completion:)
P.S. You can also design your method in such way that it has only one completion
callback with two optionals:
func getPeople(completionHandler: (([Person]?, Error?)->Void)?) {}
Personally I prefer two closures because it's more evident and readable, though it's a matter of taste.
Return Alamofire response from a function
The main problem is that Alamofire.request
is an asynchronous call, so you will need to use a completion handler, like so
func getStoriesNF (completion: @escaping ([String : Any]?, Error?) {
let parameters: Parameters = ["user_id": userID]
Alamofire.request("https://example.com/stories.php", method: .post, parameters: parameters).validate().responseJSON { response in
switch response.result {
case .success:
if let json = response.result.value {
do {
if let data = try JSONSerialization.jsonObject(with: json as Data, options: .allowFragments) as? [String:Any] {
completion(data, nil)
} catch (let error) {
completion(nil, error)
}
}
}
}
}
JSONSerialization.jsonObject
needs to be contained within a try-catch
block since it's a throwing call
How to return respond result to function with Alamofire Swift 3
You are not using closures that's why getLastModifiedDate
returns nil because it's not returning "Last-Modified"
value .You have to use closures if you are doing async task like this
func getLastModifiedDate(completion: @escaping (String) -> ()) {
var date:String?
Alamofire.request("http://www.example.com/apps/demopad/manifest.json")
.response { serverResponse in
date = serverResponse.response?.allHeaderFields["Last-Modified"] as? String
print(date ?? "Server date is nil")
self.lastModifedDateLabel.text = date
completion(date)
}
}
Return value after Alamofire dowloaded data
Your approach is correct. You could use another variable in your closure to see if the request was called properly (or another variable in function, e.g. errorHandler). Example of usage:
downloadImageFromServer(imgURL) { (data1, data2) in
print("Data1: \(data1). Data2: \(data2)")
}
Basic example of adding success/failure variable to your function:
func downloadImageFromServer(imageUrl: String, completionHandler: (Bool, String?, String?) -> ()) {
var myData1 = String()
var myData2 = String()
Alamofire.request(.GET, imageUrl)
.responseImage { response in
switch response.result {
//if user does have a photo
case .Success:
myData1 = //Something
myData2 = //Something else
completionHandler(true, myData1 as? String, myData2 as? String)
case .Failure:
completionHandler(false, nil, nil)
//Print error
}
}
}
Usage of the improved version of downloadImageFromServer()
:
downloadImageFromServer(imgURL) { (succes, data1, data2) in
if success {
print("Success. Data1: \(data1). Data2: \(data2)")
} else {
print("Error. Data1: \(data1). Data2: \(data2)")
}
}
Return a value from a Alamofire closure
You are correct. It is being returned empty because it is being run on the background thread. When making network requests we therefor tend to use completionBlocks
Swift 5 implemented the new typ of Result<Any, Error>
which is really convenient.
Try implementing completion((Result<Any, Error>) -> ())
in your function params instead. When you get the response you unwrap it my writing:
switch result {
case .succeses(let data):
//Do something
break
case .failure(let error):
//Do something
break
}
As you are inside the actual block you can't execute a return statement. That's the error you are getting. The block itself doesn't ask for a return. Unlike map, filter, reduce
etc.
Returning a value from a function with Alamofire and SwiftyJson
An example of a completion handler for your getMenu function, assuming menu
is the value you want to "return":
class MenuManager {
// the handler takes an EKMenu argument
class func getMenu(menu_id: Int, completionHandler: (menu: EKMenu) -> ()) {
let url="https://www.domain.com/arc/v1/api/menus/\(menu_id)/mobile"
Alamofire.request(.GET, url).responseJSON() {
(_, _, data, _) in
println("within menu request")
var json=JSON(data!)
var menu=EKMenu()
menu.name=json["menu"]["name"].stringValue
for (key, subJson) in json["menu"]["menu_headers"]{
EKMenu.processMenuHeaders(subJson)
}
// wrap the resulting EKMenu in the handler
completionHandler(menu)
}
}
class func processMenuHeaders(menu_header: JSON){
let mh_name=menu_header["name"].stringValue
println("mh_name: \(mh_name)")
for (key, subJson) in menu_header["menu_headers"]{
EKMenu.processMenuHeaders(subJson)
}
}
}
MenuManager.getMenu(42, completionHandler: { menu in
// here the handler gives you back the value
println(menu)
})
Related Topics
How to Sort an Nsmutablearray With Custom Objects in It
Detecting Which Uibutton Was Pressed in a Uitableview
Ios 7 Status Bar Back to iOS 6 Default Style in Iphone App
How to Symbolicate Crash Log Xcode
Get to Uiviewcontroller from Uiview
Starting Iphone App Development in Linux
Creating a Left-Arrow Button (Like Uinavigationbar'S "Back" Style) on a Uitoolbar
Tricks For Improving Iphone Uitableview Scrolling Performance
Prevent Screen Capture in an iOS App
How to Insert New Cell into Uitableview in Swift
Push Notification Issue With iOS 10
How to Detect Avplayer and Get Url of Current Video from Wkwebview
How to Pass Textfield Value to View Controller Through Button Click in Swift Ui
Iphone Hide Navigation Bar Only on First Page