Alamofire generalize request - ios

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

Related

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.

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

Expression type 'DataRequest' is ambiguous without more context Swift

I use Alamofire for my request in this function and I have this error if someone could help me please thank in advance.
Expression type 'DataRequest' is ambiguous without more context
func report(_ track: Track, completionHandler: #escaping (_ error: NSError?) -> Void) {
var headers:[String:String] = [:]
if AuthManager.defaultAuthManager().isLoggedIn() {
headers = ["Authorization": "Bearer " + AuthManager.defaultAuthManager().authToken.token!]
}
let params: [String: Any] = ["trackCode": track.code]
let urlString = Cizoo.APIBaseUrl + CizooScheme.report
CizooAPI.manager.request(urlString, method: .post, parameters: params, encoding: .JSONEncoding.default, headers: headers as HTTPHeaders)
.validate()
.responseJSON(completionHandler: { // Error at this line
response in
switch response.result {
case .success:
completionHandler(error: nil)
case .failure(let error):
completionHandler(error: error)
}
})
}
From the Alamofire source code you can see that the declaration of the method is:
#discardableResult
open func request(
_ url: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil)
-> DataRequest
{
var originalRequest: URLRequest?
do {
originalRequest = try URLRequest(url: url, method: method, headers: headers)
let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters)
return request(encodedURLRequest)
} catch {
return request(originalRequest, failedWith: error)
}
}
Which in your case probably the urlString is not conforming to the URLConvertible protocol.
Thanks !
Finally, It was a concern of type declaration
Here is the code solved :
func report(_ track: Track, completionHandler: #escaping (_ error: NSError?) -> Void) {
var headers:HTTPHeaders? = [:]
let params:Parameters? = ["trackCode": track.code]
let encoding:ParameterEncoding = JSONEncoding.default
let method:HTTPMethod = .post
let urlString:URLConvertible = (Cizoo.APIBaseUrl + CizooScheme.report)
if AuthManager.defaultAuthManager().isLoggedIn() {
headers = ["Authorization": "Bearer " + AuthManager.defaultAuthManager().authToken.token!]
}
CizooAPI.manager.request(urlString, method: method, parameters: params, encoding: encoding, headers: headers)
.validate()
.responseJSON(completionHandler: {
response in
switch response.result {
case .success:
completionHandler(nil)
case .failure(let error):
completionHandler(error as NSError?)
}
})
}

POST request with data in body with Alamofire 4

how is it possible to send a POST request with a data in the HTTP body with Alamofire 4? I used custom encoding at swift 2.3 it was working good. I converted my code swift 3 and I tried to paramater encoding but not working. This code :
public struct MyCustomEncoding : ParameterEncoding {
private let data: Data
init(data: Data) {
self.data = data
}
public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
var urlRequest = try urlRequest.asURLRequest()
do {
urlRequest.httpBody = data
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
} catch {
throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
}
return urlRequest
}
and Alamofire request :
let enco : ParameterEncoding = MyCustomEncoding(data: ajsonData)
Alamofire.request(urlString, method: .post , parameters: [:], encoding: enco , headers: headers).validate()
.responseJSON { response in
switch response.result {
case .success:
print(response)
break
case .failure(let error):
print(error)
}
}
You need to send request like below in swift 3
let urlString = "https://httpbin.org/get"
Alamofire.request(urlString, method: .post, parameters: ["foo": "bar"],encoding: JSONEncoding.default, headers: nil).responseJSON {
response in
switch response.result {
case .success:
print(response)
break
case .failure(let error):
print(error)
}
}
Swift 5 with Alamofire 5:
AF.request(URL.init(string: url)!, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: headers).responseJSON { (response) in
print(response.result)
switch response.result {
case .success(_):
if let json = response.value
{
successHandler((json as! [String:AnyObject]))
}
break
case .failure(let error):
failureHandler([error as Error])
break
}
}
Alamofire using post method
import UIKit
import Alamofire
class ViewController: UIViewController {
let parameters = [
"username": "foo",
"password": "123456"
]
let url = "https://httpbin.org/post"
override func viewDidLoad() {
super.viewDidLoad()
Alamofire.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: [:]).responseJSON {
response in
switch (response.result) {
case .success:
print(response)
break
case .failure:
print(Error.self)
}
}
}
This will work better in Swift 4.
let url = "yourlink.php". // This will be your link
let parameters: Parameters = ["User_type": type, "User_name": name, "User_email": email, "User_contact": contact, "User_password": password, "from_referral": referral] //This will be your parameter
Alamofire.request(url, method: .post, parameters: parameters).responseJSON { response in
print(response)
}
Please find the code below
**
pod 'Alamofire', '~> 5.4'
**
**
pod 'ObjectMapper', '~> 4.2'
**
**
pod 'SwiftyJSON'
**
pod 'TPKeyboardAvoiding'
Use Model
import ObjectMapper
class LoginModel : Mappable{
var status : String?
var data : [DataModel]?
var message : String?
required init?(map: Map) {
}
func mapping(map: Map) {
status <- map["status"]
data <- map["data"]
message <- map["message"]
}
}
class DataModel : Mappable{
var access_token : String?
var isvideo : String?
required init?(map: Map) {
}
func mapping(map: Map) {
access_token <- map["access_token"]
isvideo <- map["isvideo"]
}
}
Call API
HTTPNetwork().getHTTPData("", parameters: LoginParameter, completion: {(successresponse) -> Void in
if let res = successresponse{
print("sucess token \(res["message"].string!)")
if let myuser = Mapper<DataModel>().map(JSONString: res["data"].rawString()!){
print("access_token \(myuser.access_token)")
}
}
}, error: {(errorresponse)-> Void in
if let res = errorresponse{
print("Error response token \(res)")
}
})
public func getHTTPData(_ request: String, parameters : Parameters?, completion: #escaping ( JSON?) -> Void, error: #escaping ( JSON?) -> Void){
AF.request(URL.init(string: "url")!, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: ["Content-Type":"application/json"]).responseJSON { (response) in
print(response.result)
switch response.result{
case .success:
if let json = response.value as? [String : Any]{
if let output:JSON = JSON(response.value!){
if json["isSuccess"] as? Int == 1{
completion(output)
}else{
error(output)
}
}
}else{
completion(nil)
}
case .failure:
completion(nil)
}
}
}
Alamofire for GET and POST method using Alamofire
1.Create a file named "GlobalMethod" for multiple use
import Alamofire
class GlobalMethod: NSObject {
static let objGlobalMethod = GlobalMethod()
func ServiceMethod(url:String, method:String, controller:UIViewController, parameters:Parameters, completion: #escaping (_ result: DataResponse<Any>) -> Void) {
var headers = Alamofire.SessionManager.defaultHTTPHeaders
headers["HeaderKey"] = "HeaderKey"
if method == "POST" {
methodType = .post
param = parameters
}
else {
methodType = .get
}
Alamofire.request(url, method: methodType, parameters: param, encoding: JSONEncoding.default, headers:headers
).responseJSON
{ response in
completion(response)
}
}
}
In the View Controller call "ServiceMethod" created in GlobalMethod by sending values to call API Service
let urlPath = "URL STRING"
let methodType = "GET" or "POST" //as you want
let params:[String:String] = ["Key":"Value"]
GlobalMethod.objGlobalMethod.ServiceMethod(url:urlPath, method:methodType, controller:self, parameters:params)
{
response in
if response.result.value == nil {
print("No response")
return
}
else {
let responseData = response.result.value as! NSDictionary
print(responseData)
}
}

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