evermeer / AlamofireJsonToObjects

An Alamofire extension which converts JSON response data into swift objects using EVReflection
Other
161 stars 28 forks source link

[Question] What happens if the response is not of expected type? #30

Closed shaljam closed 8 years ago

shaljam commented 8 years ago

I'm using version 2.1 of Swift3 branch with Alamofire 4.0, with do following code:

Alamofire.request(Constants.base_uri + "api/register", method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: headers)
        .responseObject { (response: DataResponse<RegisterResponse>) in
        if let result = response.result.value {
           print(response.response?.statusCode)
           print(response.response?.description)
           print(response.debugDescription)
           print(result.username)
        }
        else if let error = response.result.error {
            print(error.localizedDescription)
        }

My server is Laravel and no errors occur, I'm all fine. But if any validation error occurs, response will be an something like:

{
  "username": [
    "some error.",
    "another error."
  ],
  "email": [
    "some error."
  ]
}

, and that's when I get the following exception:

 -[__NSSingleObjectArrayI length]: unrecognized selector sent to instance 0x618000011510
 *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSSingleObjectArrayI length]: unrecognized selector sent to instance 0x618000011510'

My RegisterResponse class is the following:

class RegisterResponse: EVObject {
    public var username: String?
    var firstname: String?
    var lastname: String?
    var mobile: String?
    var email: String?
    var updated_at: String?
    var created_at: String?
    var id: Int?
}
evermeer commented 8 years ago

The problem here is that you get a different data type returned for a property that also exists. How do you detect that there is an error? is it that username is an array? That is not a very secure way to detect an error. The service should have returned some kind of error object.

It's true that it should not give you an exception like this. Instead it should create a type conversion warning. I will look into this.

I can think of 2 solutions for this problem. I have to test to see if they indeed both work and what is best. I can do that later this weekend.

Solution 1: Change the username and email properties to: var username: [String]? as I can remember I do have an automatic object to array conversion. So this should work for both situations.

Solution 2:

shaljam commented 8 years ago

@evermeer

How do you detect that there is an error?

I think that should be based on response status code. If it was a 2xx then I should map to RegisterResponse, if not it would be great if I could define another class to map to or just get the string.

I'll try solution 2 and let you know the result. Update: The getSpecificType method is not being called.

Edit: How can I access status code at getSpecificType method?

evermeer commented 8 years ago

I updated the Swift 3 branch ofEVReflection to give a warning and continue by using the first element of an array instead of crashing.

You can find your issue as a unit test here: https://github.com/evermeer/EVReflection/blob/Swift3/EVReflection/EVReflectionTests/EVReflectionIssueAF30.swift#L29

The output of that test is: WARNING: Did not expect an array for email. Will use the first item instead. WARNING: Did not expect an array for username. Will use the first item instead. TestUser { hash = 7071140966282659552 email = some error. username = some error. }

evermeer commented 8 years ago

On your question: There is no way to detect the HTTP Status code at this moment. I will have a look to see what the best solution for this would be. I could inject the status code into the dictionary, but that's not very clean...

evermeer commented 8 years ago

Injecting a variable into the dictionary seems to be the best solution for this. If the http status code > 200 then it will be set in __response_statusCode You can then test for it in getSpecificType