I've just started to try out RestKit for an iOS app i'm building. I normally use ASIHttpRequest, but I want to test out RestKit mostly for its object mapping between JSON and CoreData. There are some great things about RestKit, but I've run into an issue that really makes it feel deficient, unless I'm doing something wrong or have missed something. I hope someone here can guide me on that.
I'm using RKObjectLoader to make async & sync calls to a REST API. My service is designed to send back proper HTTP status codes, along with some sort of description, a 401 being an example of when the API needs an authenticated user.
My problem is that RestKit stops acting normally if i get a 401 error back. The RKResponse object has a status code of 0, even though it has a payload in it. I'm pretty sure this comes down to NSURLConnection's poor handling of HTTP statuses, but I would expect RestKit to wrap around this somehow. Especially since the RKResponse class has quite a few wrapper functions to determine the status code of the response (isOK, isCreated, isForbidden, isUnauthorized, etc.).
In comparison, ASIHttpRequest doesn't use NSURLConnection, but instead uses the lower level CFNetwork code. ASIHttpRequest allows me to see exactly what came back over HTTP without sending out errors left & right.
Question is, am I doing something wrong, or is this the expected behavior out of RestKit? Has anyone successfully been able to make a calls to [RKResponse isAuthenticated]? Although its inconclusive to me, is there any difference between running in async and sync mode in this regard. I did read somewhere that NSURLConnection run in sync mode will act a bit differently, even though the underlying code is just calling the async operations. Does this have more to do with me using RKObjectLoader as opposed to just RKRequest? Perhaps the fact that the payload can't map to a model causes anger, but it seems that the code is breaking earlier within RKRequest.sendSynchronously, prior to when mapping actually takes place.
Bottom line is my code needs to be able to freely read HTTP status codes. Any guidance would be most appreciated.
Haider
The common way for RestKit 0.20.x is to subclass RKObjectRequestOperation.
I wrote a blog article about this problem which can be found here:
http://blog.higgsboson.tk/2013/09/03/global-request-management-with-restkit/
See http://groups.google.com/group/restkit/msg/839b84452f4b3e26
"... when authentication fails, the authentication challenge gets cancelled and that effectively voids the request."
UPDATE:
RestKit already includes a delegate method for this:
(void)request:(RKRequest *)request didFailAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
Triggers before
(void)objectLoader:(RKObjectLoader *)objectLoader didFailWithError:(NSError *)error
When HTTP Basic authentication fails, so we can use this instead.
Related
I'm handling the server trust authentication challenge in a custom way (comparing the public key from the server trust with a hashed local version) through the urlSessionDelegate, similarly to how it is done here:
How do I accept a self-signed SSL certificate using iOS 7's NSURLSession and its family of delegate methods for development purposes?
As described in the documentation, I need to cancel the challenge if I want to fail the server trust authentication myself. When doing this, my data task will complete with the NSURLErrorCancelled. I have some error handling classes which look at the resulting NSURLError returned by the datatask, and it would be a lot cleaner for me if I got NSURLErrorServerCertificateUntrusted instead.
Is there a way to achieve that? Or will those server errors only be forwarded when the authentication is handled automatically, without the delegate methods?
Would the normal handling of the certificate in question fail with that error?
If so, you could use NSURLSessionAuthChallengePerformDefaultHandling in that case.
If not, I think the only way would involve mucking around with the challenge sender, which is highly discouraged, though it might not be catastrophic if you call that and afterwards call the proper completion handler. There's a good chance you'd get two callbacks, though. I've never attempted this.
My advice would be to file a bug with Apple asking for a proper API to pass an NSError along with cancellation.
There are a wealth of resources for checking for a valid Internet connection with Swift (Reachability) and also ways to check the statusCode of the httpResponse when making an API call, but what is the "right" way to check and handle these errors (Internet not reachable, server 404) when dealing with an API heavy iOS app?
For example, when the app starts in the initial view (or AppDelegate I suppose) one can check for both and redirect to a "ServerProblemsViewController" that displays a message (or show an alert, though those can be dismissed). But what happens is someone is in the middle of using your app and the Internet drops or the server becomes unreachable? How would you handle this?
I am wondering if devs typically check for Reachability before EVERY API call and check the return status of EVERY API call, or somehow encapsulate that logic into a helper function?
How do experienced iOS devs deal with this situation?
I check for reachability on every call and then examine the response status code by type casting the NSURLResponse to an NSHTTPURLResponse (which has a statusCode property). And yes, my requests use the same base class to encapsulate all of the functionality--only creating derived classes when needed. You can use the reachability example code from Apple which has been ported to Swift and can be typically found on Github through the community. Simply reading through this repo will probably answer a lot of your questions about changes in reachability. Notifications FTW!: https://github.com/ashleymills/Reachability.swift
I'm trying to handle Alamofire errors globally, similar to this: AFNetworking: Handle error globally and repeat request
One use case would be to detect a 400 status code error (unauthorized / expired API token) and present the login page. Is there a way to hook on something in the Alamofire core or should I create my own error handler and always call validate().response() manually each time?
Thanks
At the moment you will have to handle that logic in your own custom response implementation. We (the Alamofire TC) are currently working on ways that we can make this process easier, but it's very difficult to get right without complicating the rest of the APIs. We're still a ways off yet.
With that said, I built an OAuth 2.0 system that handles this process in a different non-open-source library. It is possible, it's just difficult to do. You will need to hook into the response closure for all requests that could 401. See my answer here for a full breakdown of how to do this.
Hopefully that helps shed some light. Cheers 🍻
I’d like some input on whether there is a better design pattern to use for my iOS app, which uses a REST model to communicate asynchronously with a Django back end.
The server can presently return three types of responses to requests:
a JSON object
a server status code integer
a long Django error message
When an action is performed in the iOS app that requires data from the server, my design pattern looks like this:
An observer is added to notification center, specifying a method that can process the server response
The method puts together and sends a NSURLConnection
A NSURLConnection delegate method receives the response, does some interpretation to check what kind of server response it is, and then posts the appropriate notification to the notification center
This triggers the response method to run, processing the response
My issue with this pattern is that there are a large number of methods written to send and receive individual request and response types. For instance, if I am requesting an item list, I need to add several observers to the notification center, one to process a user list, one to process a blank user list, and one to process errors. Then I need to write custom methods for each one of those three to perform the appropriate actions and remove the observers, based on what kind of response the server sends.
Furthermore, the NSURLConnection delegate ends up being fairly complex, because I’m trying to interpret what type of a response was received (what types of items were in the list received?) without much context of what was requested, to make sure I don’t call the wrong response method when a server message comes back.
I am fairly new to both iOS programming and to REST programming, so I may be missing something obvious. Any advice or links to resources is appreciated.
I'd initially look at using RestKit to abstract your code away from the network comms so you can worry more about the data model and high level requests. Secondly, I wouldn't use notifications for this as it will likely get messy and be very hard to manage multiple simultaneous requests - delegation or block callbacks will be much better for this.
Your REST implementation is mostly server side, and emprirically you'd be passing and receiving binary. There are factors to consider, including whether you are utilizing HTTP.
Working with JSON with NSJSONSerialization class, and NSURLConnection keeps your program more lean and mean.
I am trying to access a web service, via an AFHTTPClient subclass, that has a complication
If the request succeeds, the content is returned as JSON. If it fails for some reason, the error from the server is returned formatted as XML.
At the moment, the only way I figure I can deal with this is the not attempt to use the specific XML/JSON RequestOperations, and purely treat everything as a plain HTTP request, and then attempt to parse it manually myself, depending upon what the response looks like.
Sadly, I have no control over the web service, or I'd make sure it was all JSON.
Does anybody have any better suggestions for handling this?
[EDIT]
I guess one way of making it slightly cleaner, would be creating a new subclass of AFHTTPRequestOperation, that handled the detection of content type internally, and then passed back either parsed JSON or a GDataXML object depending upon what was returned from the server.
Thanks
This might not be the cleanest or most optimal solution, but you could do a check with an existing JSON library that the response is in fact valid JSON. If it is, proceed as usual; if it isn't, treat it with your hand-carved parsing solution.