By default Alamofire treats any completed request as successful: as long as it can connect to server, and the server responds, it will consider that everything is OK.

This means you will be getting a .Success even on this situations when the HTTP status code returned on those calls corresponds to some 4xx HTTP error code.

The first time I used Alamofire I was a bit surprised by this behavior. I realized soon, though, that I had made an incorrect assumption on how the Alamofire library actually worked, and that the problem could be quickly fixed doing some small changes in my code.

How we can fix this issue

If we want Alamofire to take into account the status returned in the response we must call the function validate as explained in the Alamofire‘s documentation:

By default, Alamofire treats any completed request to be successful, regardless of the content of the response. Calling validate before a response handler causes an error to be generated if the response had an unacceptable status code or MIME type.

Notice that we would encounter a similar behavior if we were using the URLRequest class directly. Take, for example, this snippet that I used in my example on how to do Basic Authentication on Swift:

let task = URLSession.shared.dataTask(with: request) { data, response, error in
        guard let data = data, error == nil else { r
            print("\(error)")
            return
        }

        if let httpStatus = response as? HTTPURLResponse {
            // check status code returned by the http server
            print("status code = \(httpStatus.statusCode)")
    // process result
        }
    }

To determine if the request was processed correctly I did not rely only on the error variable, but I did perform an additional check of the status code returned by the server.

The same thing applies when using Alamofire; my original code in Alamofire looked like this:

let url = "https://myurl.com/posts"
Alamofire.request(url).responseJSON { response in
         switch response.result {
         case .success:
            print(response)
         case .failure(let error):
             failure(0,"Error")
         }
     }

Adding the validation call, I can specify a range of acceptable status code. If the response has an status code, other than the specified an error will be generated, and the code execution will proceed through the .failure branch

let url = "https://myurl.com/posts"
Alamofire.request(url)
.validate(statusCode: 200..<300)
.responseJSON { response in
         switch response.result {
         case .success:
            print(response)
         case .failure(let error):
             failure(0,"Error")
         }
     }

In the previous example I accepted as valid status code for my response the range in the 2xx, since that class of codes mean that the server accepted, and processed the request sucesfully.

I could narrow it down a bit, depending on the operation I am implementing, and what I know about the server responses. For example, if I am implementing a request to add a new object, I could limit the range to 201 like this:

Alamofire.request(url, method:.post, parameters:parameters,encoding: JSONEncoding.default)
.validate(statusCode: 201)
.responseJSON { response in
            switch response.result {
            case .success:
               print(response)
            case .failure(let error):
                failure(error)
            }
        }

I know that if the server can create the new object, as I requested, it will return a 201, if the server did not create the object (imagine that it cannot create the object because it already exists), it will return a different code, depending on the server implementation (a 409 conflict, or maybe even a 200).

Whatever it returns, I know that if I did not get a 201, the object was not added, and so I should let the user know.

I can also use .validate() without specifying any status codes. In that case the default behavior of Alamofire would be as follows:

Automatically validates status code within 200…299 range, and that the Content-Type header of the response matches the Accept header of the request, if one is provided.

Example:

Alamofire.request(myurl).validate().responseJSON { response in
     switch response.result {
            case .success:
               print(response)
            case .failure(let error):
                failure(error)
            }
}

Additionally, notice that .validate() can not only validate the status code, but also check that response content-type:

 .validate(contentType: ["application/json"])

That can be useful in circumstances where the server returns 200, but actually is redirecting to an HTML page.

Advertisements