Как вернуть стоимость от Alamofire
Я делаю вызовы url через API, который я создал с помощью swift следующим образом:
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
}
}
Я вызываю этот API в моем UITableViewController
, чтобы заполнить таблицу библиотекой SwiftyJSON. Однако мой returnData
из API всегда пуст. Нет проблем с вызовами Alomofire, поскольку я могу успешно получить значение. Моя проблема в том, как я должен переносить этот data
на мой контроллер табличного представления?
var api = API()
api.getOrders()
println(api.returnData) // returnData is empty
Ответы
Ответ 1
Как указывает Мэтт, 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
//
Ответ 2
Из Alamofire README (выделено мной):
Работа в Alamofire выполняется асинхронно. Асинхронное программирование может быть источником разочарования для программистов, незнакомых с концепцией, но есть очень веские причины для этого.
Вместо того, чтобы блокировать выполнение для ожидания ответа от сервера, обратный вызов указан для обработки ответа после его получения. Результат запроса доступен только внутри области действия обработчика ответа. Любое выполнение, зависящее от ответа или данных, полученных от сервера, должно выполняться в обработчике.
Ответ 3
Ниже приведен полный поток для выполнения "Действия входа" с использованием Alamofire и Swift.
Alamofire v3.3
Swift 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":"[email protected]","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, Swift 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
Класс данных импортировать Alamofire Импорт 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
}
}
}
Ответ 5
Чтобы разобрать json с помощью Swifty 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!)
}