How to catch if any failure happens in Alamofire 4 - ios

I am calling my webservice in this way.
public func prepareUrl (baseUrl: String, appendString: String, bindedParams: String, isAuthorized: Bool, method: HTTPMethod, jsonBody: [String:String], callback: #escaping (String) ->Void> Void)
{
let dm=Datamanager.sharedInstance
let baseUrl=dm.globalUrl
let urlString=baseUrl!+appendString as String+bindedParams as String
print(urlString)
Alamofire.request(urlString, method: method, parameters: nil, encoding: JSONEncoding.default)
.downloadProgress(queue: DispatchQueue.global(qos: .utility)) { progress in
print("Progress: \(progress.fractionCompleted)")
}
.validate { request, response, data in
// Custom evaluation closure now includes data (allows you to parse data to dig out error messages if necessary)
return .success
}
.responseJSON { response in
debugPrint(response)
callback("success")
}
}
But how can I do error handling here. Even If I referred the github Alamofire 4.0 migration I don't have clear idea how to do it.
Please help me.
thanks

In Alamofire gitHub has the method:
You don't need the validate, Alamofire default validation is that 200 to 299 is success, you can get the error from the response.
public func prepareUrl (baseUrl: String, appendString: String, bindedParams: String, isAuthorized: Bool, method: HTTPMethod, jsonBody: [String:String], callback: #escaping (String) ->Void> Void)
{
let dm=Datamanager.sharedInstance
let baseUrl=dm.globalUrl
let urlString=baseUrl!+appendString as String+bindedParams as String
print(urlString)
Alamofire.request(urlString, method: method, parameters: nil, encoding: JSONEncoding.default)
.downloadProgress(queue: DispatchQueue.global(qos: .utility)) { progress in
print("Progress: \(progress.fractionCompleted)")
}
.responseJSON { response in
debugPrint(response)
switch response.result {
case .success:
callback("success")
case .failure(let error):
print(error)
}
}
}
Checkout the documentation

Related

How to merge many API call into one in swift

func getCar(session: String, CarHandler: #escaping (Result<CarResponseModel, Error>) -> Void) {
let url = MAIN_URL + "/user/car"
AF.request(url, method: .post, encoding: JSONEncoding.default, headers: ["Content-Type":"application/json; charset=utf-8", "Accept":"application/json", "session":session])
.validate(statusCode: 200..<300)
.validate(contentType: ["application/json"])
.responseDecodable(of: CarResponseModel.self) { (response) in
switch response.result {
case .success(let response):
CarHandler(.success(response))
case let .failure(error):
CarHandler(error.localizedDescription)
}
}
}
func getHouse(session: String, HouseHandler: #escaping (Result<HouseResponseModel, Error>) -> Void) {
let url = MAIN_URL + "/user/house"
let parameters = ["name": "houseName"]
AF.request(url, method: .get, parameters: parameters, encoding: URLEncoding.queryString, headers: ["Content-Type":"application/json; charset=utf-8", "Accept":"application/json", "session":session])
.validate(statusCode: 200..<300)
.validate(contentType: ["application/json"])
.responseDecodable(of: HouseResponseModel.self) { (response) in
switch response.result {
case .success(let response):
HouseHandler(.success(response))
case let .failure(error):
HouseHandler(.failure(error))
}
}
}
Question
Use two functions to get car and house information. I wonder if it's possible to put this together.
Reason
The idea that if I receive information using only one communication function, I can handle the error at once.
My idea
The parameters or get, post information for handing over to the server are made by another function using communication and handed over to one communication function
Desired result
func getData(request, AllHandler: #escaping (Result<AnyModelStruct, Error>) -> Void) {
AF.request(request)
.validate(statusCode: 200..<300)
.validate(contentType: ["application/json"])
.responseDecodable(of: AnyModelStruct.self) { (response) in
switch response.result {
case .success(let response):
AllHandler(.success(response))
case let .failure(error):
AllHandler(.failure(error))
}
}
}
Problem
func getData(request, AllHandler: #escaping (Result<HouseResponseModel, Error>) -> Void) {
AF.request(request)
.validate(statusCode: 200..<300)
.validate(contentType: ["application/json"])
.responseDecodable(of: HouseResponseModel.self) { (response) in
switch response.result {
case .success(let response):
AllHandler(.success(response))
case let .failure(error):
AllHandler(.failure(error))
}
}
}
...
let url = URL(string: "http://www.stackoverflow.com")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
I know that url, method, parameters, encoding, header information can be generated and handed over as a request,
I don't know how to give and receive a model...
Model information is required for escape and response decoding. Is there a way to hand over the model information here? Or is it possible to set the Any type?
Others
Lastly, I would like to know a tip on how to proceed when I perform many API requests in other projects, if the method I'm trying now is not a popular method.
1. First, you can create a base service to generate AF requests
import Foundation
import Alamofire
class BaseService {
public static let shared = BaseService()
private var request = URLRequest(url: URL(string: Constant.API_BASE_URL)!)
private init() {}
public func generateRequest(url: String, method: HTTPMethod, body: Data?) -> URLRequest {
guard let formateUrl = URL(string: url) else {
fatalError("Invalid URL")
}
request.url = formateUrl
request.method = method
if let body_data = body {
request.httpBody = body_data
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
}
return request
}
}
2. You need to create a generic method for calling API requests
import Foundation
import Alamofire
struct API {
private static let decoder = JSONDecoder()
static func getRequest<T: Decodable>(urlPath: String, model: T.Type, completed: #escaping(Result<T, AFError>)-> Void) {
let request = BaseService.shared.generateRequest(url: urlPath, method: .get, body: nil)
AF.request(request).validate().responseDecodable(of: model.self, decoder: decoder) { (response) in
switch response.result {
case .success(let result):
completed(.success(result))
case .failure(let error):
completed(.failure(error))
}
}
}
3. After that, You can call the previous method anywhere with any JSON model
API.getRequest(urlPath: "YOUR API PATH", model: HouseResponseModel.self) { result in
switch result {
case .success(let res):
print(res)
case .failure(let error):
print(error)
}
}

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

IOS. Screened symbols in URL

I need to send a request to a server and get a response.
I have the following URL:
http://192.168.200.10:9044/api/tables/?filter={"open_visible":true,"related":false}
For the query I'm using Alamofire. Here are some ways how I do it:
1)
class func getTables(completion: #escaping (_ response : DataResponse<TablesResponse>) -> Void) {
SVProgressHUD.show()
let getTablesPath = apiEntryPoint + "tables/?filter={\"open_visible\":true,\"related\":false}"
Alamofire.request(getTablesPath)
.validate()
.responseObject { (response: DataResponse<TablesResponse>) in
SVProgressHUD.dismiss()
completion(response)
}
}
I get the error: screenshot
2)
class func getTables(completion: #escaping (_ response : DataResponse<TablesResponse>) -> Void) {
SVProgressHUD.show()
let getTablesPath = apiEntryPoint + "tables/?filter={%22open_visible%22:true,%22related%22:false}"
Alamofire.request(getTablesPath)
.validate()
.responseObject { (response: DataResponse<TablesResponse>) in
SVProgressHUD.dismiss()
completion(response)
}
}
I get the error.
3)
class func getTables(completion: #escaping (_ response : DataResponse<TablesResponse>) -> Void) {
SVProgressHUD.show()
var getTablesPath = apiEntryPoint + "tables/?filter="
let jsonParameters = ["open_visible":true, "related":false]
if let json = try? JSONSerialization.data(withJSONObject: jsonParameters, options: []) {
if let content = String(data: json, encoding: .utf8) {
getTablesPath += content
}
}
Alamofire.request(getTablesPath)
.validate()
.responseObject { (response: DataResponse<TablesResponse>) in
SVProgressHUD.dismiss()
completion(response)
}
}
I get the error.
4)
class func getTables(completion: #escaping (_ response : DataResponse<TablesResponse>) -> Void) {
SVProgressHUD.show()
let getTablesPath = apiEntryPoint + "tables/"
Alamofire.request(getTablesPath, parameters: ["open_visible":true, "related":false])
.validate()
.responseObject { (response: DataResponse<TablesResponse>) in
SVProgressHUD.dismiss()
completion(response)
}
}
I got all tables. Without taking into account the desired parameters. It's bad.
QUESTION
As I can send a request for the server, considering the necessary parameters.
Try
let urlParameters = yourParametersString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())
because you can't just pass { and } characters in URL
You can use request parameter for more user-friendly.
Put your all request data into NSDictionary and send it to the server.
let parameters: NSDictionary = [
"YOUR_KEY": YOUR_VALUE,
]
// Both calls are equivalent
Alamofire.request(YOUR_SERVER_ULR, method: .post, parameters: parameters, encoding: JSONEncoding.default)
Alamofire.request(YOUR_SERVER_ULR, method: .post, parameters: parameters, encoding: JSONEncoding(options: []))
Another way is that, if you are passing some special character into your question parameter then you have encoded the URL.
let URL = YOUR_FINAL_URL_TO_SERVER.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())
You can check out Apple Document for more detail.

How to call a method with completion handle in swift 3+

I am very new to swift. I have a method like this.
public func prepareUrl (appendString:String,bindedParams:String,isAuthorized:Bool,isGet:Bool,jsonBody:[String:String],completion:#escaping(String)->Void)
{
let baseUrl=Bundle.main.value(forKey: "GlobalURL")
let urlString=baseUrl as! String+appendString as String+bindedParams as String
Alamofire.request(urlString, method: .get, parameters: nil, encoding: JSONEncoding.default)
.downloadProgress(queue: DispatchQueue.global(qos: .utility)) { progress in
print("Progress: \(progress.fractionCompleted)")
}
.validate { request, response, data in
// Custom evaluation closure now includes data (allows you to parse data to dig out error messages if necessary)
return .success
}
.responseJSON { response in
debugPrint(response)
}
}
I have no idea how to call this method since it's having a completion handler part too. How can I call this method. Please help me.
Thanks
To call method like this:
self.prepareUrl(appendString: "www.some.com/api/likeLogin", bindedParams: "name=lee", isAuthorized: false, isGet: true, jsonBody: ["key":"value"]) { (returnString) in
if returnString == "someValue" {
//do something
}
else{
}
}
And in the method, you should call the completion to return value, like:
.responseJSON { response in
completion("aReturnString")
}
Although the method name is prepareUrl, it actually requests the WebApi, so it's better to rename it to request.
Try this :
NOTE :This answer is for example . You need to change as per your needs
func getResponse(url: String, method : HTTPMethod, parameter : [String : AnyObject], Alert : Bool, callback: #escaping responseHandler) -> Void{
Alamofire.request(API_PREFIX + url, method: method, parameters: parameter).validate().responseJSON { response in
switch response.result {
case .success:
if let result = response.result.value {
let JSON = result as! [String : AnyObject]
print("\(JSON as AnyObject)")
callback(JSON as AnyObject, true)
}
case .failure(let error):
print(error)
callback({} as AnyObject, false)
}
}
}
Calling of method using closure
self.getResponse(url: "", method: .post, parameter: ["Email" : "" as AnyObject, "Password" : "" as AnyObject], Alert: true) { (responseObject, success) in
if success{
}
}

wait for response Alamofire swift

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

Resources