I Won't Be Able to Return a Value with Alamofire in Swift

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)
})

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.

Swift alamofire refactoring trying to return status code

If you want your closure to pass back the status code, then add an Int? parameter and pass it back:

static func getCategories(_ catId: Int, response: @escaping (JSON, Int?) -> ()) {
let urlString = baseURL + ResourcePath.categories(catId: catId).description
Alamofire.request(urlString, encoding: JSONEncoding.default, headers: headers).responseJSON { responseData in
let cCatData = JSON(responseData.result.value ?? [])
response(cCatData, responseData.response?.statusCode)
}
}

Or I might use more standard variable/parameter names:

static func getCategories(_ catId: Int, completionHandler: @escaping (JSON, Int?) -> ()) {
let urlString = baseURL + ResourcePath.categories(catId: catId).description
Alamofire.request(urlString, encoding: JSONEncoding.default, headers: headers).responseJSON { response in
let cCatData = JSON(response.result.value ?? [])
completionHandler(cCatData, response.response?.statusCode)
}
}

Either way, you can then do:

Api.getCategories(catId) { json, statusCode in 
guard statusCode == 200 else {
print("status code not 200! \(statusCode)")
return
}

// if you got here, the status code must have been 200
}

Can't access Alamofire Request body

If it’s not successful, you should dive in and take a look at the error that was returned. You might even want to look at the body of the response (if its a development server, it might provide some useful error messages in the body of the response).

let group = DispatchGroup()
group.enter()
Alamofire.request(...)
.responseJSON { response in
defer { group.leave() }

switch response.result {
case .failure(let error):
print(error)
print(response.response ?? "no HTTPURLResponse")
if let data = response.data {
if let string = String(data: data, encoding: .utf8) {
print(string)
} else {
print(data as NSData)
}
} else {
print("no data")
}

case .success(let json):
print(json)
}
}

By the way, I’d suggest moving the leave call outside of any if/switch statement because you want to make sure your dispatch group is satisfied regardless of whether it was successful or not.

But, bottom line, you won’t be able to diagnose the problem until you start to look at precisely what error was returned.

Alamofire nonblocking connection

You don't want to do the networking inside your model object. Instead, you want to handle the networking layer in some more abstract object such as a Service of class methods. This is just a simple example, but I think this will really get you heading in a much better architectural direction.

import Alamofire

struct User {
let name: String
let companyName: String
}

class UserService {

typealias UserCompletionHandler = (User?, NSError?) -> Void

class func getUser(completionHandler: UserCompletionHandler) {
let loginRequest = Alamofire.request(.GET, "login/url")
loginRequest.responseJSON { request, response, json, error in
if let error = error {
completionHandler(nil, error)
} else {
println("Login Succeeded!")

let userRequest = Alamofire.request(.GET, "user/url")
userRequest.responseJSON { request, response, json, error in
if let error = error {
completionHandler(nil, error)
} else {
let jsonDictionary = json as [String: AnyObject]
let user = User(
name: jsonDictionary["name"]! as String,
companyName: jsonDictionary["companyName"]! as String
)

completionHandler(user, nil)
}
}
}
}
}
}

UserService.getUser { user, error in
if let user = user {
// do something awesome with my new user
println(user)
} else {
// figure out how to handle the error
println(error)
}
}

Since both the login and user requests are asynchronous, you cannot start using the User object until both requests are completed and you have a valid User object. Closures are a great way to capture logic to run after the completion of asynchronous tasks. Here are a couple other threads on Alamofire and async networking that may also help you out.

  • Handling Multiple Network Calls
  • Returning a Value with Alamofire

Hopefully this sheds some light.

How to get the result value of Alamofire.request().responseJSON in swift 2?

The accepted answer works great but with the introduction of Alamofire 3.0.0 there are some breaking changes that affects this implementation.

The migration guide has further explanations but i will highlight the ones related to the actual solution.

  • Response

    All response serializers (with the exception of response) return a generic Response struct.

  • Response type

    The Result type has been redesigned to be a double generic type that does not store the NSData? in the .Failure case.

Also take in count that Alamofire treats any completed request to be successful, regardless of the content of the response. So you need to chain a .validate() before .responseJSON() to hit the .Failure case.
Read more about it here.

Updated code:

let url = "http://api.myawesomeapp.com"
Alamofire.request(.GET, url).validate().responseJSON { response in
switch response.result {
case .Success(let data):
let json = JSON(data)
let name = json["name"].stringValue
print(name)
case .Failure(let error):
print("Request failed with error: \(error)")
}
}

For reference:

  • Xcode 7.3 (Swift 2.2)
  • Alamofire 3.3.1
  • SwiftyJSON 2.3.3

Swift 2 - Function not return Bool when using Alamofire

Because it run in different thread so you can't return straight. You should use callback (or another call block, closure). You should edit code:

   class userInfo: NSObject {

static func userRegistration(email: String, callBack : (emailIsavailable : Bool) -> Void) {



Alamofire.request(.GET, "https://example/check_email.php?email=\(email)")


.responseString{ response in


if let responseValue = response.result.value {

print("Response String: \(responseValue)")

if responseValue == "email is available"{


print("email available")

callBack(emailIsavailable: true)

}else{


print("email not available")

callBack(emailIsavailable: false)


}

}
}
}
}

And you can call like:

yourInstance.userRegistration("test") { (emailIsavailable) -> Void in
//you can get emailIsavaiable or something here
}


Related Topics



Leave a reply



Submit