Here is my function declaration....
func requestApi<T:Codable>(_ url: URLConvertible,method:HTTPMethod = .post, parameters: Parameters, header:[String:String] ,completion:#escaping (_ success:Bool,_ result:T?) -> Void) {
print("parameters: ",parameters)
print("HEADERS: ",header)
requestAPI(url, method:method,httpBody:parameters.queryString, headers: header) { (result:ApiResult<T,APIError>) in
print(result)
switch result {
case .success1(let result):
print(result)
completion(true, result)
case.failure(let error):
DataHandler.shared.showAlert(title: "", message: error.customDescription)
print(error.customDescription)
completion(false, nil)
}
}
}
and here is how I am calling this function.
UserStore.shared.requestApi(CUSTOMER_BOOKING_DETAILS, parameters: params, header: header) { (flag, result) in ...
And on function call I am getting this error "Generic parameter T could not be inferred , Issue"
you should give type of result in completion block
UserStore.shared.requestApi(parameters: params, header: header) { (flag, (result as? CUSTOMER_BOOKING_DETAILS)) in ...
Related
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 { }
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)
}
}
}
i'm trying to have a system where all my api requests path through one function and map them to the corresponding object
func sendApi<T>(url : String , httpMethod : HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil , callbackSuccess : #escaping (T) -> () , callbackFailure : #escaping (T) -> ()) where T : Mappable {
Alamofire.request(url, method: httpMethod, parameters: parameters , encoding: encoding, headers: headers).responseObject{(response: (DataResponse<T>))in
switch response.result {
case .success :
let result = Mapper<T>().map(JSONObject: response.value)!
callbackSuccess(result)
break;
case .failure(let error):
if((error as NSError).code == ErrorResponse.noInternetConnection){
// errorCallBack(ErrorResponse.noInternetConnectionString)
}
// errorCallBack(error.localizedDescription)
print(error.localizedDescription)
break;
}
}
}
but when i try to call the function inside a get method for example
func testApiGet(url: String , packageId : Int ,callback :#escaping (myObject) -> Void , errorCallBack : #escaping (String) -> Void ){
let token = spm.getUserToken()
let headers = ["X-Auth-Token" : token]
let newUrl = url + "?packageId=" + String(packageId)
sendApi(url: url, httpMethod: HTTPMethod.get , parameters: nil, encoding: JSONEncoding.default, headers: headers, callbackSuccess: {(jsonObject) in
} , callbackFailure:{ (jsonObject)in
})
}
i get the error "Generic parameter 'T' could not be inferred"
of course i can set the type of the object
(response: (DataResponse<myObject>)
and the error will go.
my question is how solve this error to make it fully generic
It does not make sense that your error handler and your success handler have the same type T: Mappable. You are only going to get a mapped object in the success handler. The error handler should be something like (Error) -> ().
Secondly your function is generic with respect to the parameter type of the closure arguments, but when you are calling the function you are not specifying the type:
sendApi(url: url, httpMethod: HTTPMethod.get , parameters: nil, encoding: JSONEncoding.default, headers: headers, callbackSuccess: {(jsonObject) in
} , callbackFailure:{ (jsonObject)in
})
The compiler therefore has no idea what type you are expecting and thats why its complaining that it cannot infer the type here. You need to explicitly provide the parameter type because that is what determines what version of the generic gets invoked:
sendApi(url: url, httpMethod: HTTPMethod.get , parameters: nil, encoding: JSONEncoding.default, headers: headers, callbackSuccess: {(jsonObject: MyMappableType) in
} , callbackFailure:{ (error: Error)in
})
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{
}
}
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)
}