Alamofire API request is getting failed with responseSerializationFailed in swift 3 - ios

I am calling normal API call with Alamofire, And I am not passing any data with that in networking manager class.
My Networking class is
func executeGetRequest(url:String,requestVC:UIViewController,completionHandler:#escaping (_ responseObject:Any?) -> Void!,failureHandler:#escaping (_ connectionError:NSError?) -> Void!){
//Checking internet alert
if !self.isConnectedToInternet(){
// requestVC.showAlert(kText_AppName, message: kText_NoInternet)
return
}
requestVC.showLoader()
Alamofire.request(url).responseJSON {
(response:DataResponse) in
requestVC.removeLoader()
switch(response.result) {
case .success(_):
if response.result.value != nil{
completionHandler (response.result.value)
}
break
case .failure(let error):
failureHandler (error as NSError?)
break
}
}
}
and i am calling it from my main class
kNetworkManager.executeGetRequest(url: kAppAccessTokenURL, requestVC: self, completionHandler: {
(responseObject) -> () in
print("responseObject:\(responseObject!)")
}, failureHandler: {(error)-> () in
print("response object:\(error!)")
self.showAlert(message: (error?.description)!, title: kText_AppName)
if error?._code == NSURLErrorTimedOut {
//timeout here
self.showAlert(message: kText_timeout, title: kText_AppName)
}
})
Its getting always request fail and showing error as responseSerializationFailed
if I call directly in Main Class without manager class like
Alamofire.request(kAppAccessTokenURL).responseString { response in
I am able to getting response, can anyone suggest me where getting wrong in Network class.

Here you
Alamofire.request(kAppAccessTokenURL).responseString
and there
Alamofire.request(url).responseJSON
look to that
let jsonText = "{\"first_name\":\"Sergey\"}"
var dictonary:NSDictionary?
if let data = jsonText.dataUsingEncoding(NSUTF8StringEncoding) {
do {
dictonary = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String:AnyObject]
if let myDictionary = dictonary
{
print(" First name is: \(myDictionary["first_name"]!)")
}
} catch let error as NSError {
print(error)
}
}

Related

Generic parameter 'T' could not be inferred while calling a method

I am getting below error
Generic parameter 'T' could not be inferred
I have created a method and when I am trying to call that method then getting that error. I am adding both methods below.
func requestNew<T> ( _ request: URLRequest, completion: #escaping( Result< T , NetworkError>) -> Void ) where T : Decodable {
URLCache.shared.removeAllCachedResponses()
print("URL \((request.url as AnyObject).absoluteString ?? "nil")")
//use the currentrequest for cancel or resume alamofire request
currentAlamofireRequest = self.sessionManager.request(request).responseJSON { response in
//validate(statusCode: 200..<300)
if response.error != nil {
var networkError : NetworkError = NetworkError()
networkError.statusCode = response.response?.statusCode
if response.response?.statusCode == nil{
let error = (response.error! as NSError)
networkError.statusCode = error.code
}
//Save check to get the internet connection is on or not
if self.reachabilityManager?.isReachable == false {
networkError.statusCode = Int(CFNetworkErrors.cfurlErrorNotConnectedToInternet.rawValue)
}
completion(.failure(networkError))
}else{
print("response --- > ",String(data: response.data!, encoding: .utf8) ?? "No Data found")
if let responseObject = try? JSONDecoder().decode(T.self, from: response.data!) {
completion(.success(responseObject.self))
}else {
}
}
}
}
Below is screenshot of error
![
]1
func getVersion1(complete :#escaping (Response<Version>) -> Void, failure:#escaping onFailure) {
self.network.requestNew(self.httpRequest) { (result) in
print("hello")
}
When Swift cannot infer the generic parameter, although it accepts the generic declaration of the method, you can specify the type by passing type fixed parameters.
Try this:
func getVersion1(complete :#escaping (Response<Version>) -> Void, failure:#escaping onFailure) {
self.network.requestNew(self.httpRequest) { (result: Result<Version, NetworkError>) in
print("hello")
}
}
You may need to change Result<Version, NetworkError> to Result<SomeDecodableType, NetworkError>, if Version is not the type you expect from the request.

Alamofire best way to recall api on failure

I need to recall the api when receiving an specific error say error code -1005.
I want to handle this in Alamofire files so that it can work with all api in project.
I was handling this in ObjC's AFNetworking in dataTaskWithHTTPMethod with below code:-
if (failure)
{
if (error.code == -1005)
{
[self POST:URLString parameters:parameters progress:nil success:success failure:failure];
}
}
Can anyone help me to do this in Alamofire?
lets understand with One Example.
let me guess as you have created one class for managing all the webservice realted stuff there. (if not yet then it will be good to create one for best practice).
Okay now create two typealias for managing response.
here it is :-
here i assume again that you want whole dictionary for success response and error for failure response.
typealias successCompletion = (([String:Any]) -> ())
typealias failureCompletion = ((Error) -> ())
now here its one WSManager Class for handling all the API Related stuff there.
class AlamofireManager {
static func sampleFunctionOfWebService(successCompletion:successCompletion , failureCompletion:failureCompletion) {
if success {
successCompletion(["Key":"success"])
} else {
failureCompletion(-1005 as! Error)
}
}
}
you need to pass the both typealias in function for getting CallBack
in desired class.
Here for only understanding purpose we are going to pass static
dictionary :-> ["Key":"success"] and static -1005 as Error.
now how to use this function in our desired class?
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func wsCalling() {
AlamofireManager.sampleFunctionOfWebService(successCompletion: { (dict) in
print(dict)
}) { (error) in
if error.code == -1005 {
self.wsCalling() // recall API Again
} else {
// your other logic
}
}
}
}
I have not mentioned here for URLSeeionTask and All , its good thing
to manage URLSeeionTask. If you have a instance of URLSeeionTask of
Previous API then cancel it first and then try to recall it again.
Happy Coding.
//MARK: Your method
func hitYourService(){
showActivityIndicator(decision: true, inViewConroller: self, animated: true)
let deviceToken = HelpingClass.userDefaultForKey(key: "deviceToken")
let paramDict = ["email":txtFEmail.text ?? "" ,"password":txtFPassword.text ?? "" ,"device_token":deviceToken]
NetworkManager.instance().hitPostServiceJsonForm(paramDict, myReqURL: ServiceMethods.serviceBaseURL+ServiceMethods.login, unReachable: {
// Here you can handle the case if there is no internet connection
}) { (response) in
showActivityIndicator(decision: false, inViewConroller: self, animated: true)
if response != nil{
if response?["error_code"] as! Int == 0{
//Handle your response
}else{
//Show error
showHudWithMessage(message: response?["error_msg"] as! String, inViewConroller: self, animated: true, hideAfter: 2)
}
}else{
//You can hit back your service
self.hitYourService()
}
}
}
//MARK: Alamofire method to hit service, written in NetworkManager class
func hitPostServiceJsonForm(_ params:Dictionary<String,Any>, myReqURL:String, unReachable:(() -> Void),handler:#escaping ((Dictionary<String,Any>?) -> Void)) {
if networkReachable() == false {
unReachable()
}
var request = URLRequest(url: URL(string: myReqURL)!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
print_debug(object: "ReqParameter: \(String(describing: params))") // http url response
//JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
request.httpBody = try! JSONSerialization.data(withJSONObject: params, options: [])
Alamofire.request(request).responseJSON { response in
// SwiftLoader.hide()
print_debug(object: "Request: \(String(describing: response.request))") // original url request
print_debug(object: "Response: \(String(describing: response.response))") // http url response
print_debug(object: "Result: \(response.result)") // response serialization result
print_debug(object: "Result.value \(String(describing: response.result.value))")
switch response.result {
case .success:
if let jsonDict = response.result.value as? Dictionary<String,Any> {
print_debug(object: "Json Response: \(jsonDict)") // serialized json response
handler(jsonDict)
}
else{
handler(nil)
}
if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) {
print("Server Response: \(utf8Text)") // original server data as UTF8 string
}
break
case .failure(let error):
handler(nil)
print_debug(object: error)
break
}
}
}

error in return response 'cannot call value of non-function type 'NSHTTPURLResponse?' with Swift 3

I read answer from other questions also but not able to solve:
I tried with following way but getting same error at the line of
return response(responseSerializer: responseSerializer,completionHandler: completionHandler)
please help us in how you add #escaping in following method.
public func JSONResponseObject<T: ResponseObjectSerializable>(_ completionHandler: #escaping (DataResponse<T>) -> Void) -> Self {
let responseSerializer = DataResponseSerializer<T> { request, response, data, error in
guard error == nil else { return .failure(error!) }
let jsonResponseSerializer = DataRequest.jsonResponseSerializer(options: .allowFragments)
let result = jsonResponseSerializer.serializeResponse(request, response, data, nil)
print("result: \(result.value)")
switch result {
case .success(let value):
let json = JSON(value)
print("JSON: \(json)")
if let
response = response,
let responseObject = T(response: response, representation: value as AnyObject)
{
return .success(responseObject)
} else {
let error = Alamofire.AFError.ResponseSerializationFailureReason.jsonSerializationFailed(error: -6006 as! Error)
return .failure(error as! Error)
}
case .failure(let error):
let json = JSON(error)
print("JSON: \(json)")
return .failure(error)
}
}
return response(responseSerializer: responseSerializer,completionHandler: completionHandler)
}
I solved now in following way
I set extension DataRequest { } instead in extension Request { }

Alamofire 4.0 error from server

Hi I just migrated to alamofire 4 and I just want to send the error coming from the server, I found a couple ways but I just want to make sure that this is the correct way, here is my custom responseobject class
public protocol ResponseObject {
init?(response: HTTPURLResponse, representation: Any)
}
enum BackendError: Error {
case network(error: Error)
case dataSerialization(error: Error)
case jsonSerialization(error: Error)
case xmlSerialization(error: Error)
case objectSerialization(reason: String)
}
extension DataRequest {
public typealias Validation = (URLRequest?, HTTPURLResponse, Data?) -> ValidationResult
func responseObject<T: ResponseObject>(
queue: DispatchQueue? = nil,
completionHandler: #escaping (DataResponse<T>) -> Void)
-> Self
{
let responseSerializer = DataResponseSerializer<T> { request, response, data, error in
guard error == nil else {
let jsonResponseSerializer = DataRequest.jsonResponseSerializer(options: .allowFragments)
let result = jsonResponseSerializer.serializeResponse(request, response, data, nil)
debugPrint(result)
return .failure(BackendError.network(error: error!))
}
let jsonResponseSerializer = DataRequest.jsonResponseSerializer(options: .allowFragments)
let result = jsonResponseSerializer.serializeResponse(request, response, data, nil)
guard case let .success(jsonObject) = result else {
return .failure(BackendError.jsonSerialization(error: result.error!))
}
guard let response = response, let responseObject = T(response: response, representation: jsonObject) else {
return .failure(BackendError.objectSerialization(reason: "JSON could not be serialized: \(jsonObject)"))
}
return .success(responseObject)
}
return response(queue: queue, responseSerializer: responseSerializer, completionHandler: completionHandler)
}
}
I add a debugprint so see the error from the server and I see it but do I have to serialize de data again inside the error?? and how can I pass the message to my custom error?

Chain multiple Alamofire requests

I'm looking for a good pattern with which I can chain multiple HTTP requests. I want to use Swift, and preferrably Alamofire.
Say, for example, I want to do the following:
Make a PUT request
Make a GET request
Reload table with data
It seems that the concept of promises may be a good fit for this. PromiseKit could be a good option if I could do something like this:
NSURLConnection.promise(
Alamofire.request(
Router.Put(url: "http://httbin.org/put")
)
).then { (request, response, data, error) in
Alamofire.request(
Router.Get(url: "http://httbin.org/get")
)
}.then { (request, response, data, error) in
// Process data
}.then { () -> () in
// Reload table
}
but that's not possible or at least I'm not aware of it.
How can I achieve this functionality without nesting multiple methods?
I'm new to iOS so maybe there's something more fundamental that I'm missing. What I've done in other frameworks such as Android is to perform these operations in a background process and make the requests synchronous. But Alamofire is inherently asynchronous, so that pattern is not an option.
Wrapping other asynchronous stuff in promises works like this:
func myThingy() -> Promise<AnyObject> {
return Promise{ fulfill, reject in
Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"]).response { (_, _, data, error) in
if error == nil {
fulfill(data)
} else {
reject(error)
}
}
}
}
Edit: Nowadays, use: https://github.com/PromiseKit/Alamofire-
I wrote a class which handles a chain of request one by one.
I created a class RequestChain wich takes Alamofire.Request as parameter
class RequestChain {
typealias CompletionHandler = (success:Bool, errorResult:ErrorResult?) -> Void
struct ErrorResult {
let request:Request?
let error:ErrorType?
}
private var requests:[Request] = []
init(requests:[Request]) {
self.requests = requests
}
func start(completionHandler:CompletionHandler) {
if let request = requests.first {
request.response(completionHandler: { (_, _, _, error) in
if error != nil {
completionHandler(success: false, errorResult: ErrorResult(request: request, error: error))
return
}
self.requests.removeFirst()
self.start(completionHandler)
})
request.resume()
}else {
completionHandler(success: true, errorResult: nil)
return
}
}
}
And I use it like this
let r1 = Alamofire.request(Router.Countries).responseArray(keyPath: "endpoints") { (response: Response<[CountryModel],NSError>) in
print("1")
}
let r2 = Alamofire.request(Router.Countries).responseArray(keyPath: "endpoints") { (response: Response<[CountryModel],NSError>) in
print("2")
}
let r3 = Alamofire.request(Router.Countries).responseArray(keyPath: "endpoints") { (response: Response<[CountryModel],NSError>) in
print("3")
}
let chain = RequestChain(requests: [r1,r2,r3])
chain.start { (success, errorResult) in
if success {
print("all have been success")
}else {
print("failed with error \(errorResult?.error) for request \(errorResult?.request)")
}
}
Importent is that you are telling the Manager to not execute the request immediately
let manager = Manager.sharedInstance
manager.startRequestsImmediately = false
Hope it will help someone else
Swift 3.0 Update
class RequestChain {
typealias CompletionHandler = (_ success:Bool, _ errorResult:ErrorResult?) -> Void
struct ErrorResult {
let request:DataRequest?
let error:Error?
}
fileprivate var requests:[DataRequest] = []
init(requests:[DataRequest]) {
self.requests = requests
}
func start(_ completionHandler:#escaping CompletionHandler) {
if let request = requests.first {
request.response(completionHandler: { (response:DefaultDataResponse) in
if let error = response.error {
completionHandler(false, ErrorResult(request: request, error: error))
return
}
self.requests.removeFirst()
self.start(completionHandler)
})
request.resume()
}else {
completionHandler(true, nil)
return
}
}
}
Usage Example Swift 3
/// set Alamofire default manager to start request immediatly to false
SessionManager.default.startRequestsImmediately = false
let firstRequest = Alamofire.request("https://httpbin.org/get")
let secondRequest = Alamofire.request("https://httpbin.org/get")
let chain = RequestChain(requests: [firstRequest, secondRequest])
chain.start { (done, error) in
}
You have multiple options.
Option 1 - Nesting Calls
func runTieredRequests() {
let putRequest = Alamofire.request(.PUT, "http://httpbin.org/put")
putRequest.response { putRequest, putResponse, putData, putError in
let getRequest = Alamofire.request(.GET, "http://httpbin.org/get")
getRequest.response { getRequest, getResponse, getData, getError in
// Process data
// Reload table
}
}
}
This is definitely the approach I would recommend. Nesting one call into another is very simple and is pretty easy to follow. It also keeps things simple.
Option 2 - Splitting into Multiple Methods
func runPutRequest() {
let putRequest = Alamofire.request(.PUT, "http://httpbin.org/put")
putRequest.response { [weak self] putRequest, putResponse, putData, putError in
if let strongSelf = self {
// Probably store some data
strongSelf.runGetRequest()
}
}
}
func runGetRequest() {
let getRequest = Alamofire.request(.GET, "http://httpbin.org/get")
getRequest.response { [weak self] getRequest, getResponse, getData, getError in
if let strongSelf = self {
// Probably store more data
strongSelf.processResponse()
}
}
}
func processResponse() {
// Process that data
}
func reloadData() {
// Reload that data
}
This option is less dense and splits things up into smaller chunks. Depending on your needs and the complexity of your response parsing, this may be a more readable approach.
Option 3 - PromiseKit and Alamofire
Alamofire can handle this pretty easily without having to pull in PromiseKit. If you really want to go this route, you can use the approach provided by #mxcl.
Here is another way to do this (Swift 3, Alamofire 4.x) using a DispatchGroup
import Alamofire
struct SequentialRequest {
static func fetchData() {
let authRequestGroup = DispatchGroup()
let requestGroup = DispatchGroup()
var results = [String: String]()
//First request - this would be the authentication request
authRequestGroup.enter()
Alamofire.request("http://httpbin.org/get").responseData { response in
print("DEBUG: FIRST Request")
results["FIRST"] = response.result.description
if response.result.isSuccess { //Authentication successful, you may use your own tests to confirm that authentication was successful
authRequestGroup.enter() //request for data behind authentication
Alamofire.request("http://httpbin.org/get").responseData { response in
print("DEBUG: SECOND Request")
results["SECOND"] = response.result.description
authRequestGroup.leave()
}
authRequestGroup.enter() //request for data behind authentication
Alamofire.request("http://httpbin.org/get").responseData { response in
print("DEBUG: THIRD Request")
results["THIRD"] = response.result.description
authRequestGroup.leave()
}
}
authRequestGroup.leave()
}
//This only gets executed once all the requests in the authRequestGroup are done (i.e. FIRST, SECOND AND THIRD requests)
authRequestGroup.notify(queue: DispatchQueue.main, execute: {
// Here you can perform additional request that depends on data fetched from the FIRST, SECOND or THIRD requests
requestGroup.enter()
Alamofire.request("http://httpbin.org/get").responseData { response in
print("DEBUG: FOURTH Request")
results["FOURTH"] = response.result.description
requestGroup.leave()
}
//Note: Any code placed here will be executed before the FORTH request completes! To execute code after the FOURTH request, we need the request requestGroup.notify like below
print("This gets executed before the FOURTH request completes")
//This only gets executed once all the requests in the requestGroup are done (i.e. FORTH request)
requestGroup.notify(queue: DispatchQueue.main, execute: {
//Here, you can update the UI, HUD and turn off the network activity indicator
for (request, result) in results {
print("\(request): \(result)")
}
print("DEBUG: all Done")
})
})
}
}
Details
Alamofire 4.7.2
PromiseKit 6.3.4
Xcode 9.4.1
Swift 4.1
Full Sample
NetworkService
import Foundation
import Alamofire
import PromiseKit
class NetworkService {
static fileprivate let queue = DispatchQueue(label: "requests.queue", qos: .utility)
fileprivate class func make(request: DataRequest) -> Promise <(json: [String: Any]?, error: Error?)> {
return Promise <(json: [String: Any]?, error: Error?)> { seal 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):
DispatchQueue.main.async {
seal.fulfill((nil, error))
}
case .success(let data):
DispatchQueue.main.async {
seal.fulfill(((data as? [String: Any]) ?? [:], nil))
}
}
}
}
}
class 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)
}
}
Main func
func run() {
_ = firstly {
return Promise<Void> { seal in
DispatchQueue.global(qos: .background).asyncAfter(deadline: DispatchTime.now() + .seconds(2)) {
print("1 task finished")
DispatchQueue.main.async {
seal.fulfill(Void())
}
}
}
}.then {
return NetworkService.searchRequest(term: "John").then { json, error -> Promise<Void> in
print("2 task finished")
//print(error ?? "nil")
//print(json ?? "nil")
return Promise { $0.fulfill(Void())}
}
}.then {_ -> Promise<Bool> in
print("Update UI")
return Promise { $0.fulfill(true)}
}.then { previousResult -> Promise<Void> in
print("previous result: \(previousResult)")
return Promise { $0.fulfill(Void())}
}
}
Result
You can use the when method in PromiseKit to attach/append as many calls you want.
Here's an example from PromiseKit docs:
firstly {
when(fulfilled: operation1(), operation2())
}.done { result1, result2 in
//…
}
It worked perfectly for me and it's a much cleaner solution.
Call itself infinitely and DEFINE END CONDITION.
urlring for API link and Dictionary for json
WE may construct the queue model or delegate
func getData(urlring : String , para : Dictionary<String, String>) {
if intCount > 0 {
Alamofire.request( urlring,method: .post, parameters: para , encoding: JSONEncoding.default, headers: nil) .validate()
.downloadProgress {_ in
}
.responseSwiftyJSON {
dataResponse in
switch dataResponse.result {
case .success(let json):
print(json)
let loginStatus : String = json["login_status"].stringValue
print(loginStatus)
if loginStatus == "Y" {
print("go this")
print("login success : int \(self.intCount)")
self.intCount-=1
self.getData(urlring: urlring , para : para)
}
case .failure(let err) :
print(err.localizedDescription)
}
}
}else{
//end condition workout
}
}

Resources