wait for response Alamofire swift - ios

I need to wait for response.response?.allHeaderFields data before executing function. I've searched the net and didn't quite get how to add "completion handler" to alamofire request. Or if there are any other ways to make the function wait.
#IBAction func comfirmation(sender: UIButton) {
if CodeTextField.text != "" {
print("in comfirmation function")
let comfirmationRequestData = [
"phone" : "\(appDelegate.savedNumber)",
"code" : "\(CodeTextField.text!)"
]
Alamofire.request(.POST,
"http://192.168.214.241:4000/login",
parameters: comfirmationRequestData,
encoding: .JSON).responseJSON {
response in
switch response.result {
case .Success:
let jsonDecrypted = JSON(response.result.value!)
print(jsonDecrypted)
let headerFile = response.response?.allHeaderFields as? [String:String]
print(headerFile)
case .Failure(let error):
print(error)
}
}
print("in comfirmation function. success")
appDelegate.defaults.setValue(appDelegate.savedNumber, forKey: "phoneNumber")
} else {
print("in comfirmation function. failed")
}
}

Use Alamofire like this
func postRequest( urlSuffix : String, params:[String : AnyObject]?, filterParams : [String]?, success: (response: AnyObject!) -> Void, failure: (error: NSError?) -> Void)
{
Alamofire.request(.POST, webServicesURLPrefix + urlSuffix, parameters: params, encoding: .JSON, headers: self.headers)
request?.responseJSON { response in
switch response.result
{
case .Success:
success(response: response.result.value)
case .Failure(let error):
failure(error: error)
}
}
}
Call the method from anywhere as
self.postRequest("do-registration.php", params: params, filterParams: nil, success: { (response) -> Void in
self.afterResponse(response)
}) { (error) -> Void in
failure(error: error)
}
OR you can write a saperate method which you will have to call after the completion.
func afterResponse(responseData : AnyObject)
{
print("Done")
print(responseData)
}

You can cause the operation to be synchronous, but in order to do that you are going to have to use a semaphore for that you set up prior to the Alamofire request, and that you then release within the completion handler. You will wait on the semaphore right after you initiate Alamo fire.

There is a library Alamofire-Synchronous which works using semaphore.
Sample:
//json
let response = Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON()
if let json = response.result.value {
print(json)
}

Related

How to write alamofire request function that returns the response?

I am writing a function to call a POST request using AlamoFire. I am passing URL and parameters. I need to return the response of the Alamofire request.
This is my code:
func callAPI(params: Dictionary<String, Any>, url: String) -> Void {
let hud = MBProgressHUD.showAdded(to: self.view, animated: true)
hud.contentColor = UIColor.red
DispatchQueue.global().async {
Alamofire.request(url, method: .post, parameters: params, encoding: JSONEncoding(options: []), headers: nil).responseJSON { response in
DispatchQueue.main.async {
hud.hide(animated: true)
switch response.result{
case .success:
if let resultJson = response.result.value as? Dictionary<String,Any>{
print(resultJson)
// Success
}
case .failure(let error):
print(error)
//Failed
}
}
}
}
}
I want to return the response dictionary resultJson from this function. And I want to reuse this function for all API calls.
How can I rewrite this function to get the solution?
You can pass a closure as a parameter to the function like this
func callAPI(params: Dictionary<String, Any>, url: String, completion:#escaping (((Dictionary<String,Any>?) -> Void))) -> Void {
let hud = MBProgressHUD.showAdded(to: self.view, animated: true)
hud.contentColor = UIColor.red
Alamofire.request(url, method: .post, parameters: params, encoding: JSONEncoding(options: []), headers: nil).responseJSON { response in
hud.hide(animated: true)
switch response.result{
case .success:
if let resultJson = response.result.value as? Dictionary<String,Any>{
print(resultJson)
completion(resultJson)
// Success
}
case .failure(let error):
print(error)
completion(nil)
//Failed
}
}
}
Call the function with the closure
callAPI(params: [:], url: "") { resultJson in
guard let resultJson = resultJson else {
return
}
print(resultJson)
}
You should pass the clouser parameter.
After that when success execute completion(resultJson, nil) if server result error you should execute completion(nil, error.localizedDescription)
func callAPI(params: Dictionary<String, Any>, url: String , completion: #escaping (Dictionary<String,Any>?, String?) -> ()) -> Void { }

Alamofire generalize request

I am using Almofire for all my requests and works fine. I need to know how to generalize all requests to handle all errors at one place.
func updateSettingValue(group : String , value: String , callback: #escaping (SettingsResponseModel) -> Void, errorCallback: #escaping (Error) ->Void)
{
let url = BASE_URL_PROD + API_SETTINGS
let settingsParams : Parameters = ["Setting" : group , "Tag" : value]
Alamofire.request(url, method: .put, parameters: settingsParams, headers: getHeader()).responseObject {
(response: DataResponse< SettingsResponseModel>) in
switch response.result {
case .success:
print("response \(response)")
DispatchQueue.main.async {
callback(response.result.value!)
}
break
case .failure(let error):
print(error)
errorCallback(error)
}
}
}
func releaseKeys(mKey: String ,callback: #escaping (ReleaseKeyModel) -> Void
, errorCallback: #escaping (Error) -> Void){
let url = BASE_URL_PROD + API_RELEASE_KEY
let params: Parameters = ["mKey" : mKey]
Alamofire.request(url, method: .delete, parameters: params, encoding: URLEncoding.default, headers: getHeader()).responseObject{
(response : DataResponse< ReleaseKeyModel >) in
print("releaseKey: \(response) ")
switch response.result {
case .success:
DispatchQueue.main.async {
callback(response.result.value!)
}
break
case .failure(let error):
print(error)
errorCallback(error)
}
}
}
How can I generalize this to take parameters for Mapping Model class in DataResponse so that I don't have to handle success and failure case individually for all methods.
You can divide the work with the server into 2 classes:
1) class RestClient
import Foundation
typealias responseBlock = (_ swiftObj: Any?, _ error: Error?) -> Void
class RestClient: NSObject {
static let shared = RestClient()
private var http = HttpService()
func updateSettingValue(group: String, value: String, resp: #escaping responseBlock) {
let url = BASE_URL_PROD + API_SETTINGS
let params = ["Setting": group, "Tag": value]
http.reque(url, method: .put, parameters: params, headers: getHeader(), resp: { (value, error) in
if let err = error {
return resp(nil, err)
}
guard let data = value else {
return resp(nil, error)
}
//your method for parse data
self.parseData(respData: data,
modelCls: SettingsResponseModel.self,
response: resp)
})
}
func releaseKeys(mKey: String, resp: #escaping responseBlock) {
let url = BASE_URL_PROD + API_RELEASE_KEY
let params = ["mKey": mKey]
http.reque(url, method: .delete, parameters: params, encoding: URLEncoding.default, headers: getHeader(), resp: { (value, error) in
if let err = error {
return resp(nil, err)
}
guard let data = value else {
return resp(nil, error)
}
//your method for parse data
self.parseData(respData: data,
modelCls: ReleaseKeyModel.self,
response: resp)
})
}
}
2) class HttpService
class HttpService {
func reque(_ url: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil,
queue: QueueQos = .defaultQos,
resp: #escaping responseBlock) {
Alamofire.request(url,
method: method,
parameters: parameters,
encoding: encoding,
headers: headers
).responseObject (queue: queue) { (response) in
switch response.result {
case .success:
if let jsonResp = response.result.value {
//You can also check out some error messages at this place.
resp(jsonResp, nil)
}
case .failure(let error):
resp(nil, error)
}
}
}
}
Try this, using generics
func releaseKeys<T: Codable>(parameters params: [String: Any], callback: #escaping (T) -> Void
, errorCallback: #escaping (Error) -> Void){
Alamofire.request(url, method: .delete, parameters: params, encoding: URLEncoding.default, headers: getHeader()).responseObject{
(response : DataResponse< T >) in
print("releaseKey: \(response) ")
switch response.result {
case .success:
DispatchQueue.main.async {
callback(response.result.value!)
}
break
case .failure(let error):
print(error)
errorCallback(error)
}
}
}

Swift app using Stripe - Type 'STPCustomer' has no member 'decodedObject'

I am trying to integrate Stripe into an app using the model that they provided in the Swift iOS (Simple) example on their GitHub (Available At: https://github.com/stripe/stripe-ios).
I added both Stripe and Alamofire to a new project that I am building through Cocoapods, and everything works perfectly excluding one function in the MyAPIClient.swift file. This function is:
#objc func retrieveCustomer(_ completion: #escaping STPCustomerCompletionBlock) {
let url = self.baseURL.appendingPathComponent("customer")
Alamofire.request(url)
.validate(statusCode: 200..<300)
.responseJSON { response in
switch response.result {
case .success(let result):
if let customer = STPCustomer.decodedObject(fromAPIResponse: result as? [String: AnyObject]) { //Error occurs here
completion(customer, nil)
} else {
completion(nil, NSError.customerDecodingError)
}
case .failure(let error):
completion(nil, error)
}
}
}
On the line including the "if let customer" statement, an error is thrown, explains that Type 'STPCustomer' has no member 'decodedObject'. I am not certain if I added the frameworks incorrectly or did something else incorrectly, as this statement works perfectly in Stripe's swift example project. Here is the entirety of the file in question:
import Foundation
import Stripe
import Alamofire
class MyAPIClient: NSObject, STPBackendAPIAdapter {
static let sharedClient = MyAPIClient()
var baseURLString: String? = nil
var baseURL: URL {
if let urlString = self.baseURLString, let url = URL(string: urlString) {
return url
} else {
fatalError()
}
}
func completeCharge(_ result: STPPaymentResult, amount: Int, completion: #escaping STPErrorBlock) {
let url = self.baseURL.appendingPathComponent("charge")
Alamofire.request(url, method: .post, parameters: [
"source": result.source.stripeID,
"amount": amount
])
.validate(statusCode: 200..<300)
.responseString { response in
switch response.result {
case .success:
completion(nil)
case .failure(let error):
completion(error)
}
}
}
#objc func retrieveCustomer(_ completion: #escaping STPCustomerCompletionBlock) {
let url = self.baseURL.appendingPathComponent("customer")
Alamofire.request(url)
.validate(statusCode: 200..<300)
.responseJSON { response in
switch response.result {
case .success(let result):
if let customer = STPCustomer.decodedObject(fromAPIResponse: result as? [String: AnyObject]) {
completion(customer, nil)
} else {
completion(nil, NSError.customerDecodingError)
}
case .failure(let error):
completion(nil, error)
}
}
}
#objc func selectDefaultCustomerSource(_ source: STPSourceProtocol, completion: #escaping STPErrorBlock) {
let url = self.baseURL.appendingPathComponent("customer/default_source")
Alamofire.request(url, method: .post, parameters: [
"source": source.stripeID,
])
.validate(statusCode: 200..<300)
.responseString { response in
switch response.result {
case .success:
completion(nil)
case .failure(let error):
completion(error)
}
}
}
#objc func attachSource(toCustomer source: STPSourceProtocol, completion: #escaping STPErrorBlock) {
let url = self.baseURL.appendingPathComponent("customer/sources")
Alamofire.request(url, method: .post, parameters: [
"source": source.stripeID,
])
.validate(statusCode: 200..<300)
.responseString { response in
switch response.result {
case .success:
completion(nil)
case .failure(let error):
completion(error)
}
}
}
}
extension NSError {
static var customerDecodingError: NSError {
return NSError(domain: StripeDomain, code: 50, userInfo: [
NSLocalizedDescriptionKey: "Failed to decode the Stripe customer. Have you modified the example backend?"
])
}
}
Any help is appreciated! Thanks!
Okay, I'm an idiot. Stripe Pushed update 10.1.0, but didn't make the new branch for their 10.1.0 sample project the default one. Everything is golden now.

Swift: passing values from different scopes in closures

Evening, I'm fetching the data from an API, and I'm having problems to passing the masons through different scopes:
I have this fun in my controller:
func getDaily() {
let json = NetworkManager.getDaily()
print(json)
}
And this one in a NetworkManager Class
class func getDaily() -> JSON {
//Setting the url for the request
let url = "\(base_url)planetary/apod?api_key=\(apy_key)"
var json: JSON = []
//Making the request
Alamofire.request(url, method: .get).validate().responseJSON{ response in
switch response.result {
case .success(let value):
json = JSON(value)
//print("JSON: \(json)")
case .failure(let error):
print(error)
}
}
return json
}
and obviously the json printing in the first func is always empty.
Can you please explain me the way to do this?
Your function getDaily() should not be returning JSON. Because this is an async request you need a callback. Try it like this:
class func getDaily(result: #escaping (JSON) -> ()) {
//Setting the url for the request
let url = "\(base_url)planetary/apod?api_key=\(apy_key)"
var json: JSON = []
//Making the request
Alamofire.request(url, method: .get).validate().responseJSON{ response in
switch response.result {
case .success(let value):
json = JSON(value)
result(json)
case .failure(let error):
print(error)
}
}
}
Your caller would then become:
func getDaily() {
NetworkManager.getDaily { json in
print(json)
}
}
For me this is the correct way:
import UIKit
import Alamofire
class APIManager: NSObject {
static let sharedInstance = APIManager()
//TODO :-
/* Handle Time out request alamofire */
func requestGETURL(_ strURL: String, success:#escaping (JSON) -> Void, failure:#escaping (Error) -> Void)
{
Alamofire.request(strURL).responseJSON { (responseObject) -> Void in
//print(responseObject)
if responseObject.result.isSuccess {
let resJson = JSON(responseObject.result.value!)
//let title = resJson["title"].string
//print(title!)
success(resJson)
}
if responseObject.result.isFailure {
let error : Error = responseObject.result.error!
failure(error)
}
}
}
func requestPOSTURL(_ strURL : String, params : [String : AnyObject]?, headers : [String : String]?, success:#escaping (JSON) -> Void, failure:#escaping (Error) -> Void){
Alamofire.request(strURL, method: .post, parameters: params, encoding: JSONEncoding.default, headers: headers).responseJSON { (responseObject) -> Void in
if responseObject.result.isSuccess {
let resJson = JSON(responseObject.result.value!)
success(resJson)
}
if responseObject.result.isFailure {
let error : Error = responseObject.result.error!
failure(error)
}
}
}
}
/********************************* To get response in another class ********************************/
APIManager.sharedInstance.requestPOSTURL(HttpsUrl.Address, params: dict as [String : AnyObject]?, headers: nil, success: { (json) in
// success code
print(json)
}, failure: { (error) in
//error code
print(error)
})

Alamofire completion handler not being called

I'm currently trying to call a prepareForSegue method in an AlamoFire completion handler but it's not being called. Here is my code:
func loginMember (username: String, password: String, completionHandler: (String?, ErrorType?) -> ()) {
let headers = [
"Cache-Control": "no-cache",
"Content-Type": "application/json"
]
let parameters: [String: AnyObject] = [
"grant_type" : "password",
"username" : username,
"password" : password,
]
Alamofire.request(.POST, "\(baseURL)/oauth2/token", parameters: parameters, encoding: .JSON, headers: headers)
.validate()
.responseJSON { response in
switch response.result {
case .Success:
guard let value = response.result.value else {
completionHandler(nil, response.result.error)
return
}
let swiftyJsonVar = JSON(value)
accessToken = swiftyJsonVar["access_token"].stringValue
print("This is the login response:\(swiftyJsonVar)")
case .Failure(let error):
print("Sorry there was an error: \(error)")
return
}
}
}
This is what it looks like when called:
loginMember(username, password: password, completionHandler: { error in
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("loginToHomeSegue", sender: self)
}
}
)
Any ideas as to why the performSegueWithIdentifier isn't being called?
You're only calling your completion handler in the case where you enter your guard statement. You need to add calls for the case where you get your access token and your error case.
Alamofire.request(.POST, "\(baseURL)/oauth2/token", parameters: parameters, encoding: .JSON, headers: headers)
.validate()
.responseJSON { response in
switch response.result {
case .Success:
guard let value = response.result.value else {
completionHandler(nil, response.result.error)
return
}
let swiftyJsonVar = JSON(value)
accessToken = swiftyJsonVar["access_token"].stringValue
print("This is the login response:\(swiftyJsonVar)")
// Got the token, call handler
completonHandler(accessToken, nil)
case .Failure(let error):
print("Sorry there was an error: \(error)")
// Got an error, call handler
completionHandler(nil, error)
return
}
}

Resources