如何从 Alamofire 返回值
Posted
技术标签:
【中文标题】如何从 Alamofire 返回值【英文标题】:How to return value from Alamofire 【发布时间】:2014-12-09 23:15:38 【问题描述】:我正在通过我使用 swift 创建的 API 进行 url 调用,如下所示:
class API
let apiEndPoint = "endpoint"
let apiUrl:String!
let consumerKey:String!
let consumerSecret:String!
var returnData = [:]
init()
self.apiUrl = "https://myurl.com/"
self.consumerKey = "my consumer key"
self.consumerSecret = "my consumer secret"
func getOrders() -> NSDictionary
return makeCall("orders")
func makeCall(section:String) -> NSDictionary
let params = ["consumer_key":"key", "consumer_secret":"secret"]
Alamofire.request(.GET, "\(self.apiUrl)/\(self.apiEndPoint + section)", parameters: params)
.authenticate(user: self.consumerKey, password: self.consumerSecret)
.responseJSON (request, response, data, error) -> Void in
println("error \(request)")
self.returnData = data! as NSDictionary
return self.returnData
我在UITableViewController
中调用此 API 以使用 SwiftyJSON 库填充表格。但是我来自 API 的returnData
始终为空。 Alomofire 调用没有问题,因为我可以成功检索值。我的问题是我应该如何将这个data
带到我的表视图控制器上?
var api = API()
api.getOrders()
println(api.returnData) // returnData is empty
【问题讨论】:
与您的原始问题无关,我对您如何使用消费者密钥和秘密(您正在使用 Alamofireauthenticate
进程,但对参数)。这可能是其中之一。这是您的网络服务 API 的一个功能,因此我们无法在此回答,但这几乎可以肯定是不正确的。
我使用的服务API是这样设计的。文档建议将密钥和机密作为用户名和密码传递以进行基本身份验证。
好吧,如果真的使用BASIC认证,那么使用authenticate
函数,但是params
字典的目的是什么?嘿,不管用什么,但同时执行authenticate
并再次将身份验证详细信息作为参数传递给查询似乎很奇怪......
【参考方案1】:
正如 mattt 所指出的,Alamofire 正在通过“完成处理程序”模式异步返回数据,因此您必须这样做。您不能立即return
值,而是希望更改您的方法以不返回任何内容,而是使用完成处理程序闭包模式。
现在,可能看起来像:
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")
然后,当你想调用它时,你使用这个 completion
闭包参数(如果你想的话,在尾随闭包中):
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
//
【讨论】:
正确,这不会“返回”任何东西。它不能,因为它是异步运行的。例如,在viewDidLoad
中调用getOrders
,在getOrders
完成块(我打印responseObject
)中,您将(a)使用异步调用的结果更新您的属性; (b) 从该完成闭包调用[self.tableView reloadData]
。因此,您的 UITableViewDataSource
方法最终将被调用两次,一次是在您第一次加载它时(此时还没有数据可用);在您致电reloadData
之后再次。
您的代码看起来不错,但给我这个错误。由于信号导致命令失败:分段错误:11 为什么会这样?
@Granola - 啊,我误会了。 Xcode 给你的是分段错误,而不是你的应用程序。看起来更新后的 Alamofire 语法会导致编译器出现问题。您可以使用switch
语句独立检查.Success
和.Failure
,这样就解决了这个问题。它没有那么紧凑,但对 Alamofire Result
enum
的处理更加清晰。
@Mitesh - 请参阅上面的修改示例。看起来 Alamofire 的变化导致 Xcode 崩溃。我已经修改了我的答案来解决这个 Xcode 错误。
完美!谢谢你。我的代码现在更加清晰和正确。对于所有异步调用,这应该是方式。如果您想将响应解析为 json,只需进行更改:而不是 completionHandler(value as? NSDictionary, nil)
,只需执行 completionHandler(value, nil)
并将 getOrders
和 makeCall
中的 NSDictionary
替换为 AnyObject
。 value 是 responseObject 的原样。您现在可以像这样在处理程序中解析这个值:let json = JSON(responseObject!)
【参考方案2】:
来自 Alamofire README(已添加重点):
Alamofire 中的网络是异步完成的。异步编程可能会让不熟悉该概念的程序员感到沮丧,但这样做有很好的理由。
不是阻止执行以等待来自服务器的响应,而是指定一个回调来处理收到的响应。 请求的结果仅在响应处理程序的范围内可用。任何取决于从服务器接收到的响应或数据的执行都必须在处理程序中完成。
【讨论】:
【参考方案3】:以下是使用 Alamofire 和 Swift 执行“登录操作”的完整流程。
Alamofire v3.3 斯威夫特 2.2 Xcode 7.3
为了自己的方便,我使用了 GCD 和 MBProgressHUD。随心所欲地重构和使用:)
func loginBtnTapped(sender: AnyObject)
MBProgressHUD.showHUDAddedTo(self.view, animated: true)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0))
let loginInfo : Dictionary<String,AnyObject> = ["email":"abc@g.com","password":"abc123"]
self.loginUser(loginInfo) responseObject, error in
print("\(responseObject) \n \(error) ")
// Parsing JSON Below
let status = Int(responseObject?.objectForKey("status") as! String)
if status == 1
// Login Successfull...Move To New VC
else
print(responseObject?.objectForKey("message"))! as! String)
return
dispatch_async(dispatch_get_main_queue())
MBProgressHUD.hideHUDForView(self.view, animated: true)
func loginUser(parameters:NSDictionary, completionHandler: (NSDictionary?, NSError?) -> ())
self.postRequest("http://qa.company.com/project/index.php/user/login",
paramDict: parameters as? Dictionary<String, AnyObject>,
completionHandler: completionHandler)
func postRequest(urlString: String, paramDict:Dictionary<String, AnyObject>? = nil,
completionHandler: (NSDictionary?, NSError?) -> ())
Alamofire.request(.POST, urlString, parameters: paramDict)
.responseJSON response in
switch response.result
case .Success(let JSON):
completionHandler(JSON as? NSDictionary, nil)
case .Failure(let error):
completionHandler(nil, error)
【讨论】:
【参考方案4】:详情
xCode 9.1,斯威夫特 4
特点:
易于阅读的代码 现成的模板(添加更多请求很容易) 具有异步数据处理的嵌入式解决方案 完整示例样本 1
使用闭包返回数据
Data1.searchRequest(term: "jack johnson") json, error in
print(error ?? "nil")
print(json ?? "nil")
print("Update views")
完整样本 1
数据类
import Alamofire
class Data1
static fileprivate let queue = DispatchQueue(label: "requests.queue", qos: .utility)
static fileprivate let mainQueue = DispatchQueue.main
fileprivate class func make(request: DataRequest, closure: @escaping (_ json: [String: Any]?, _ error: Error?)->())
request.responseJSON(queue: Data1.queue) response in
// print(response.request ?? "nil") // original URL request
// print(response.response ?? "nil") // HTTP URL response
// print(response.data ?? "nil") // server data
//print(response.result ?? "nil") // result of response serialization
switch response.result
case .failure(let error):
Data1.mainQueue.async
closure(nil, error)
case .success(let data):
Data1.mainQueue.async
closure((data as? [String: Any]) ?? [:], nil)
class func searchRequest(term: String, closure: @escaping (_ json: [String: Any]?, _ error: Error?)->())
let request = Alamofire.request("https://itunes.apple.com/search?term=\(term.replacingOccurrences(of: " ", with: "+"))")
Data1.make(request: request) json, error in
closure(json, error)
UIViewController
class ViewController: UIViewController
override func viewDidLoad()
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Data1.searchRequest(term: "jack johnson") json, error in
print(error ?? "nil")
print(json ?? "nil")
print("Update views")
样本 2
使用委托返回数据
// ....
var data = Data2()
data.delegate = self
data.searchRequest(term: "jack johnson")
// ....
extension ViewController: Data2Delegate
func searchRequest(response json: [String : Any]?, error: Error?)
print(error ?? "nil")
print(json ?? "nil")
print("Update views")
完整样本 2
数据类
import Alamofire
protocol Data2Delegate: class
func searchRequest(response json: [String: Any]?, error: Error?)
class Data2
fileprivate let queue = DispatchQueue(label: "requests.queue", qos: .utility)
fileprivate let mainQueue = DispatchQueue.main
weak var delegate: Data2Delegate?
fileprivate func make(request: DataRequest, closure: @escaping (_ json: [String: Any]?, _ error: Error?)->())
request.responseJSON(queue: queue) response in
// print(response.request ?? "nil") // original URL request
// print(response.response ?? "nil") // HTTP URL response
// print(response.data ?? "nil") // server data
//print(response.result ?? "nil") // result of response serialization
switch response.result
case .failure(let error):
self.mainQueue.async
closure(nil, error)
case .success(let data):
self.mainQueue.async
closure((data as? [String: Any]) ?? [:], nil)
func searchRequest(term: String)
let request = Alamofire.request("https://itunes.apple.com/search?term=\(term.replacingOccurrences(of: " ", with: "+"))")
make(request: request) json, error in
self.delegate?.searchRequest(response: json, error: error)
UIViewController
import UIKit
class ViewController: UIViewController
private var data = Data2()
override func viewDidLoad()
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
data.delegate = self
data.searchRequest(term: "jack johnson")
extension ViewController: Data2Delegate
func searchRequest(response json: [String : Any]?, error: Error?)
print(error ?? "nil")
print(json ?? "nil")
print("Update views")
样本 3
使用PromiseKit返回数据
_ = data.searchRequest(term: "jack johnson").then response in
print(response.error ?? "nil")
print(response.json ?? "nil")
print("Update views")
return .void
完整样本 3
数据类 进口阿拉莫火 导入 PromiseKit
class Data3
fileprivate let queue = DispatchQueue(label: "requests.queue", qos: .utility)
fileprivate let mainQueue = DispatchQueue.main
fileprivate func make(request: DataRequest) -> Promise<(json:[String: Any]?, error: Error?)>
return Promise fulfill, reject in
request.responseJSON(queue: queue) response in
// print(response.request ?? "nil") // original URL request
// print(response.response ?? "nil") // HTTP URL response
// print(response.data ?? "nil") // server data
//print(response.result ?? "nil") // result of response serialization
switch response.result
case .failure(let error):
self.mainQueue.async
fulfill((nil, error))
case .success(let data):
self.mainQueue.async
fulfill(((data as? [String: Any]) ?? [:], nil))
func searchRequest(term: String) -> Promise<(json:[String: Any]?, error: Error?)>
let request = Alamofire.request("https://itunes.apple.com/search?term=\(term.replacingOccurrences(of: " ", with: "+"))")
return make(request: request)
extension AnyPromise
class var void: AnyPromise
return AnyPromise(Promise<Void>())
UIViewController
import UIKit
import PromiseKit
class ViewController: UIViewController
private var data = Data3()
override func viewDidLoad()
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
_ = data.searchRequest(term: "jack johnson").then response in
print(response.error ?? "nil")
print(response.json ?? "nil")
print("Update views")
return .void
【讨论】:
我喜欢示例 1,但我很困惑 .. 如何插入标题、正文和方法? 所有示例都已完成且可以正常工作。创建一个新项目并试验代码。向date class
添加新查询。以及使用代码放在UIViewController class
【参考方案5】:
要使用 Swifty JSON 解析 json,我是这样做的。 对于@Jenita _Alice4Real
func uploadScans(parameters: [String: AnyObject], completionHandler: (AnyObject?, NSError?) -> ())
makePostCall(CommonFunctions().getSaveSKUDataUrl(), parameters: parameters,completionHandler: completionHandler)
func makePostCall(url: String, parameters: [String: AnyObject], completionHandler: (AnyObject?, NSError?) -> ())
Alamofire.request(.POST, url, parameters: parameters)
.responseJSON response in
switch response.result
case .Success(let value):
completionHandler(value, nil)
case .Failure(let error):
completionHandler(nil, error)
uploadScans(params) responseObject, error in
let json = JSON(responseObject!)
【讨论】:
以上是关于如何从 Alamofire 返回值的主要内容,如果未能解决你的问题,请参考以下文章
使用 Alamofire 和 SwiftyJson 从函数返回值