1

First our codes

let req = SignUp()
req.loginName = "abc@abc.com"
req.passWord = "xxx"

do{
    let resp = try client.put(req)   <---Where we had an error
} catch {
    //some error handling
    //.....
}

And then, when we input the correct information, everything is fine but when the login credential is wrong, we had expected to get an 401 error with a proper error message, we didn't. And when we traced it trying to find the origin of this, we had traced it back to JsonServicClient.swift (generated from ServiceStack swift plugin for Xcode 7.2), line 266.

public func send<T : JsonSerializable>(intoResponse:T, request:NSMutableURLRequest) throws -> T {
    var response:NSURLResponse? = nil

    var data = NSData()
    do {
        data = try NSURLConnection.sendSynchronousRequest(request, returningResponse: &response)

        var error:NSError? = NSError(domain: NSURLErrorDomain, code: NSURLErrorUnknown, userInfo: nil)
        if let dto = self.handleResponse(intoResponse, data: data, response: response!, error: &error) {
                                                                  //^^^error here, response is null/can't be null
            return dto
        }
        if let e = error {
            throw e
        }
        return T()
    } catch var ex as NSError? {
        if let e = self.handleResponse(intoResponse, data: data, response: response!, error: &ex) {
            return e
        }
        throw ex!
    }
}

So here is the question, how to properly use ServiceStack swift plugin to get 401 error? When the service returns 400, everything is fine. This problem only happens when the servicestack server api returns 401. Which is by design, we supposed to return 401 when a user authentication fails.

shawhu
  • 1,217
  • 1
  • 12
  • 27
  • Strange, response shouldn't be `nil` on the marked line, however it should fail in the `catch` block. – Sulthan Feb 22 '16 at 03:46
  • @Sulthan Yet it is what we have witnessed right now. I guess there must be something wrong with the generated JsonServiceClient.swift code. The error handling is not working. – shawhu Feb 22 '16 at 03:58
  • The code in the error handler is obviously wrong. It also uses API deprecated on iOS 9 (`sendSynchronousRequest`). Generated code is always a bit of a risk. – Sulthan Feb 22 '16 at 04:00
  • 1
    The JsonServiceClient.swift code is pulled in from remote source, not generated. Only the client DTOs are generated (by the ServiceStack server and pulled in locally). Code for JsonServiceClient is on GH. https://github.com/ServiceStack/ServiceStack.Swift/blob/master/dist/JsonServiceClient.swift . Does the route associated with the `SignUp` dto require authentication? Are you able to show the related .NET service code? – Darren Reid Feb 22 '16 at 06:58
  • @Layoric Sorry for the example, signup is really not a good name, imagine we are dealing with sign_in, where user enters username and password, the app post it with json to the api written with servicestack. And in the api, we just put some standard validation where compare the password with hashed value in db, etc, and when error happens, we just throw an error. like such: throw new AuthenticationException("password error something"); – shawhu Feb 23 '16 at 03:40

1 Answers1

1

This is a bug in Swift's sendSynchronousRequest which sometimes returns a null response which makes it impossible to determine what the server error response was. I've added a fix for this issue in this commit where it will now throw an unknown Error instead of segfaulting but as Swift doesn't return a HTTP Response we can't query it to return any more info about the error, e.g:

do {
    try client.post(request)
} catch let responseError as NSError {
    //Swift Bug: 401 returns an unitialized response so status is nil
    if let status:ResponseStatus = responseError.convertUserInfo() {
    }
}

To get the latest version you can either replace to latest JsonServiceClient.swift or delete the JsonServiceClient.swift and Add/Remove a new Service Reference which will download the latest version.

If you can, you can also switch to use the Async API's which doesn't have this issue:

client.postAsync(request)
    .error { responseError in
        let status:ResponseStatus = responseError.convertUserInfo()!
        status.errorCode //= Unauthorized 
    }
Community
  • 1
  • 1
mythz
  • 141,670
  • 29
  • 246
  • 390
  • That `catch` handler is really strange, it force-unwraps `response` too although it most cases `response` would be `nil` there. Are you sure that's correct? – Sulthan Feb 22 '16 at 20:06
  • It never reaches the force unwrap if response = nil which is the point of the change. The catch handles the Unknown error not nil and it doesn't unwrap – mythz Feb 22 '16 at 20:18
  • I am not talking about the change, I mean the original catch handler `catch var ex as NSError? {` which is unwrapping `response!` on the first line. That doesn't seem right. – Sulthan Feb 22 '16 at 20:34
  • Have you tried it? It's not unwrapping the response, its handling the Unknown NSError that gets thrown. Sounds like you're confused about the `as` operator used for catching NSError - that's not unwrapping that's catching/handling Errors that can be casted to `NSError`. – mythz Feb 22 '16 at 23:42
  • Thanks both. We are testing. Here is the result. The error happened when using sync post is gone but still we can't get 401 error. Of course it's a Swift issue therefore still waiting for Apple to handle it (have we notified Apple?) And we are testing the postasync to see if we can use that instead. One request is if we can be directed to a page where we can find all the proper usages of those methods. Like postasync, now we learn how to use it thru your test cases... – shawhu Feb 23 '16 at 03:47
  • @shawhu The docs of the API's available are on the [Swift JsonServiceClient wiki](https://github.com/ServiceStack/ServiceStack/wiki/Swift-Add-ServiceStack-Reference#swift-client-usage) which provides examples of the different types of API's available, most of the other API's are just sent with different verbs. Otherwise tests in [ServiceStackClientTests](https://github.com/ServiceStack/ServiceStack.Swift/tree/master/src/ServiceStackClient/ServiceStackClientTests) provides a good source of reference as well. – mythz Feb 23 '16 at 04:12
  • @shawhu Apple has already handled this case: NSURLConnection is deprecated. Using synchronous calls for networking was deprecated since software has been invented ;) I would strongly suggest, the folks at ServiceStack would deprecate/remove their synchronous version as well. – CouchDeveloper Feb 23 '16 at 15:47
  • @CouchDeveloper Remove existing API's and break existing customer solutions?! Absolutely not. Those API's will be removed when they're removed from iOS, which since Apple isn't actively hostile to their developer-base will likely be never. – mythz Feb 23 '16 at 17:05
  • @mythz I know the testcase is very good. In fact that is the first place we are looking. About the deprecated api, is there a way that, maybe you can mark the sync post method to be pending for deprecation? Other than that, we don't have question, everything works when we switched to use postasync method. Thanks!! – shawhu Feb 24 '16 at 07:02
  • @shawhu Swift deprecated their HTTP sync API so all sync methods are deprecated. But they provide a easier programming model in background threads than using async APIs and are often preferred. If you're on the UI thread you should only be using the non-blocking Async APIs which most App devs would already know. It should be clear when to use either so we don't have any plans deprecating them until we know they will be removed in a future version of iOS. – mythz Feb 24 '16 at 07:32