Inconsistent response types with ngResource in UIWebView
July 14th 2014
While working on a mobile hybrid application, we recently ran into an issue where in some browsers, the correct data was not being returned from our $resource
correctly.
var Data = $resource("/data/:id", {}, {
all: {
method: "GET",
responseType: "json",
isArray: true,
transformResponse: function(response) {
return response.names;
}
}
});
Data.all().$promise.then(console.log);
In desktop browsers it was giving us the expected array of names. But in iOS 7, it was actually returning just an empty array.
The output in Chrome
{
"names": [{"id": 1, name: "rokkincat"}, {"id":2, name: "Rockin Catz"}]
}
The output in iOS 7
[]
The difference is subtle, chrome is parsing the data into a JavaScript object, and iOS7 is just passing the responseText through. Then when we try to access the names
attribute on response (a string) it gives us undefined
. Then since we had isArray
set to true, it finaggled undefined
into []
, which is not helpful. Even more infuriating is that if you are on the developer preview of iOS8, you get the following:
The output in iOS 8
{
"names": [{"id": 1, name: "rokkincat"}, {"id":2, name: "Rockin Catz"}]
}
The problem comes from a partial implementation of XMLHttpRequest
in the webkit bundled in iOS 7, specifically not supporting the responseType
property. Angular passes your value for responseType
directly to the XHRHttpRequest
without any polyfill.
To get around this, you can either forego giving a responseType
and just parse the JSON yourself in transformResponse
or you can put the following injector on the $http
module which will add responseType
support to browsers that don’t have it. This code can technically go on any of your modules, but I would suggest putting it near your factory or service that is exposing the $resource
.
.config(function($httpProvider) {
var jsonResponseParser = function(response) {
if(typeof response === "string") { response = JSON.parse(response); }
return response;
}
$httpProvider.interceptors.push(function() {
return {
request: function(request) {
if(request.responseType === "json") {
if(typeof request.transformResponse === "function") {
request.transformResponse = [jsonResponseParser, request.transformResponse];
} else {
request.transformResponse.push(jsonResponseParser);
}
}
return request;
}
}
})
})