PDA

View Full Version : Sencha PhoneGap using proxy to read JSON



stevenpsmith
8 Feb 2011, 9:12 AM
I am in the process of taking a Sencha App and wrapping it in PhoneGap. This app loads JSON data from a file using an ajax proxy. When creating the PhoneGap project for Android, everything works fine without changing any code. When trying to create an iOS application, the data will not load. It just hangs with the loading spinner spinning. Here is the code:



Demo.stores.Speakers = new Ext.data.Store({
storeId: 'SpeakerStore',
model : 'Speaker',
autoLoad: true, //calls load after Store creation, otherwise would need to load in code
proxy: {
type: 'ajax',
url:'speakers.json'
}
});


The speakers.json file is included with the other assets within the www folder of the PhoneGap project.

If I replace the proxy config with a data attribute and some hard coded JSON, everything works fine. Any help is appreciated.

doncoleman
10 Feb 2011, 6:15 PM
I had the same problem with an offline webapp if the json file was added to the CACHE MANIFEST.

Safari returns status === 0 for the ajax call even though it is returning the correct data.

I solved this temporarily by registering a new proxy based on the AjaxProxy



// With Safari 5.0.3 if a resource is added to the CACHE MANIFEST we always
// get a status of 0 from Ajax calls, even though the responseText is returned
// from the cache. This object just assumes the call was successful.
var SafariOfflineAjaxProxy = Ext.extend(Ext.data.AjaxProxy, {

// based on AjaxProxy::createRequestCallback
createRequestCallback: function(request, operation, callback, scope) {
var me = this;

return function(options, success, response) {
// Safari always returns status === 0 when json file
// is in the manifest. Assume success.
var reader = me.getReader(),
result = reader.read(response);

Ext.apply(operation, {
response : response,
resultSet: result
});

operation.setCompleted();
operation.setSuccessful();

if (typeof callback == 'function') {
callback.call(scope || me, operation);
}

me.afterRequest(request, true);
};
}
});

Ext.data.ProxyMgr.registerType('offlineajax', SafariOfflineAjaxProxy);

Nertus
11 Feb 2011, 10:13 AM
doesn't work

doncoleman
11 Feb 2011, 12:33 PM
To get this to work, you need to change the your proxy type from 'ajax' to 'offlineajax'

Nertus
11 Feb 2011, 2:31 PM
It's clear. But if i use Ext.Ajax.request it's doesn't work.

doncoleman
11 Feb 2011, 3:25 PM
My modified proxy doesn't change what's happening with the Ajax request, it just ignores the error that comes back. This works good for me but you might need a different solution.

Check the Sencha source code. Ext.Ajax is a Ext.data.Connection.

Set a breakpoint in Ext.data.Connection::onComplete

Look at the values in r.xhr

That's where I saw the status of 0 but r.xhr.responseText was valid. Check responseXml too.

I tried a couple of solutions that intercepted this call and attempted to modify XMLHttpRequest but I couldn't modify the status.

An alternate solution is to add the following code to the top of Ext.data.Connection::onComplete



if (status === 0) {
status = 200;
}


I couldn't find a good way to monkey patch this in. Editing the sencha source works. Extending Ext.Ajax works too, but then I needed to modify other code where this was called. So I decided to create a new proxy.

It looks like this is a Webkit bug. Hopefully Safari will get fixed or the Sencha team will come up with a work around in a future version.

SimonFlack
16 Mar 2011, 5:36 AM
Just ran into this issue myself... Anyone know the latest and best solution?

mvea
10 Jun 2011, 12:02 AM
I'm having this problem as well. Can anyone let me know how to resolve this? Thanks in advance.

martinbroos
10 Jun 2011, 1:17 AM
I'm having the same problem see thread : My-nested-list-doesn-t-showup-on-iPhone-iOS-but-does-work-in-safari (http://www.sencha.com/forum/showthread.php?136086-My-nested-list-doesn-t-showup-on-iPhone-iOS-but-does-work-in-safari)

Still no solution but i checked the input you guys gave and its true ... When i serve my json as a static file copied directly from the php encode function out of the browser it works!

But dynamically with the json output from my php file it doesn't work on iOs. Works fine on android and safari on browser. Keep getting a json parser error while its perfectly fine json.
I stripped the code down to some simple values and still an error.

The only thing i can conclude is that the json encode function from php screws up and the browser output fixes it ... or i'm i missing something

martinbroos
10 Jun 2011, 1:23 AM
To be exact in my case it has nothing to do with running in offline mode. Even online it has troubles with reading my served json from a server. But works fine when its a static file.

ncooley527
14 Jul 2011, 10:51 PM
But works fine when its a static file.
@martin -- What did you use to get a static JSON file to be read by iOS? Currently, I'm trying to use an AjaxProxy to get a file that's local to my index.html, but no luck so far... Any ideas? What did you use?

martinbroos
15 Jul 2011, 4:43 AM
To get the static file I runt my webservice script from within the browser wich outputs the JSON. Copy all of the json code and placed in http://jsonlint.com/ to make sure its valid and it formats it nicely.

After that i created a new file on my server json.php and placed all the json code in it.

after that i just redirected my ajax proxy to this file instead of the webservice :



beheerpaneel.stores.Pages = new Ext.data.TreeStore({
model: 'beheerpaneel.models.Pages',
autoLoad: false,
proxy: {
type: 'ajax',
url: '/mobile/json.php',
reader: {
type: 'tree',
root: 'items'
}
}
});


Still haven't solve this problem though. On most iPhones it just doesn't load from a webservice to parse the json. Also in the simulator it failes. But on most iPad and iPhone 4 it works fine :-?

Hopes this helpes you, and if you have a solution to the problem please post it .

fx-mike
18 Jul 2011, 8:08 AM
This is how I solved the ajax request problem with a static file for myself:



Ext.Ajax.request({
url: whatever,
success: function(response, opts) {
doStuff(response.responseText);
},
failure: function(response, opts) {
if(phoneGapAvailable && response.status == 0 && response.responseText != ''){
doStuff(response.responseText);
} else {
console.error('failed to complete request');
}
}
});




phoneGapAvailable: function() {
return typeof(PhoneGap) != "undefined";
}


In short, I just check if I'm in phonegap and if I got some responseText in the failure callback. This way I know that I can safely use the responseText to do whatever I would do in the success callback.
You'll probably want to use your own phonegap check or remove it completely if you don't deploy your app as a web app.

Cheers

pupx
9 Oct 2011, 11:25 PM
Is there any solution for this? I've an example project with a scripttag proxy but it only works in safari. If I try to run it in Xcode 4 in a phonegap project it doesn't get any data.

Would be great if someone have a solution. I just want to get data from a server into my application.

Thank you

janfma
11 Oct 2011, 4:27 AM
Used the "offlineajax" trick and it worked perfect :-)

Thank you!!

Frode:)

Cromamedia
3 Jan 2012, 12:09 PM
Hi everyone, just in case you are dealing with this problem in ST2, here you have the few changes I've done to make it work:


Ext.define('Ext.data.SafariOfflineAjaxProxy', {
extend: 'Ext.data.AjaxProxy',
alias : 'proxy.offlineajax',

// based on AjaxProxy::createRequestCallback
createRequestCallback: function(request, operation, callback, scope) {
var me = this;

return function(options, success, response) {
// Safari always returns status === 0 when json file
// is in the manifest. Assume success.
var reader = me.getReader(),
result = reader.read(response);

Ext.apply(operation, {
response : response,
resultSet: result
});

operation.setCompleted();
operation.setSuccessful();

if (typeof callback == 'function') {
callback.call(scope || me, operation);
}

me.afterRequest(request, true);
};
}
});

After that, just use:



proxy: {
type: 'offlineajax',
url: 'sample.json',
reader : {
type : 'json'
}
}

8alery
26 Jan 2012, 10:59 PM
Thank you very much!
I used your solution in 1.x project and now I will use it in my new 2.x project!

LikeWinter
31 Jan 2012, 2:15 AM
The solution stops working in the new version (PR4 instead of PR3 where everything was good), anyone have any ideas why? Thanks.

Cromamedia
1 Feb 2012, 4:38 AM
Based on the new implementation of AjaxProxy::createRequestCallback. Hope it helps.


Ext.define('Ext.data.SafariOfflineAjaxProxy', {
extend: 'Ext.data.AjaxProxy',
alias : 'proxy.offlineajax',

// based on AjaxProxy::createRequestCallback
createRequestCallback: function(request, operation, callback, scope) {
var me = this;

return function(options, success, response) {
var action = operation.getAction(),
reader, resultSet;

// Safari always returns status === 0 when json file
// is in the manifest. Assume success.
reader = me.getReader();
resultSet = reader.process(me.extractResponseData(response));

if (operation.process(action, resultSet, request, response) === false) {
this.fireEvent('exception', this, response, operation);
}

//this callback is the one that was passed to the 'read' or 'write' function above
if (typeof callback == 'function') {
callback.call(scope || me, operation);
}

me.afterRequest(request, success);
};
}
});

mike lebowski
2 Feb 2012, 8:25 AM
Hi Cromamedia, thanks for pointing me to this thread from the 2.x forum. Do you know if your suggestion works in the just released 2.x beta as well?

Cromamedia
2 Feb 2012, 8:47 AM
Hi Mike. That was a modification only for PR4. The code for the Beta 1:


Ext.define('Ext.data.SafariOfflineAjaxProxy', {
extend: 'Ext.data.AjaxProxy',
alias : 'proxy.offlineajax',

// based on AjaxProxy::createRequestCallback
createRequestCallback: function(request, operation, callback, scope) {
var me = this;

return function(options, success, response) {
var action = operation.getAction(),
reader, resultSet;

reader = me.getReader();

try {
resultSet = reader.process(response);
} catch(e) {
operation.setException(operation, {
status: null,
statusText: e.getMessage()
});

me.fireEvent('exception', this, response, operation);
return;
}


if (operation.process(action, resultSet, request, response) === false) {
this.fireEvent('exception', this, response, operation);
}

//this callback is the one that was passed to the 'read' or 'write' function above
if (typeof callback == 'function') {
callback.call(scope || me, operation);
}

me.afterRequest(request, success);
};
}
});

In the Store:


proxy: {
type: 'offlineajax',
url: 'sample.json',
reader : {
type : 'json'
}
}

mike lebowski
5 Feb 2012, 9:48 PM
Wait a minute Croma, your proxy workaround worked great with SP3. Then I decided to move to the b1 beta.

I am using the sencha-touch-debug.js
I added the alert code you suggest to catch errors.

I look at your code above, and the version you suggest for beta has your workaround class extending Ext.data.AjaxProxy, but Ext.data.AjaxProxy does not exist in b1 ... it has moved to Ext.data.proxy.Ajax, no ?
Should your code really say "extends Ext.data.proxy.Ajax"?

Cromamedia
6 Feb 2012, 12:41 AM
Hi Mike,

The Ext.data.AjaxProxy classname can be used as an alternate name of Ext.data.proxy.Ajax. I didn't realise about this name change tho. http://docs.sencha.com/touch/2-0/#!/api/Ext.data.proxy.Ajax

However, it is better to use the new official classname so I will update the original post.
http://www.sencha.com/forum/showthread.php?177913-Problem-accessing-a-local-data-file-using-Phonegap-and-ST-2.0&p=725784

Is your application working?

mike lebowski
6 Feb 2012, 9:49 AM
Hi Chroma,

Still stuck in migration from PR3 to b1. Still getting white screen.
You can see current blocker here:
http://www.sencha.com/forum/showthread.php?150431-Ext.ux.touch.grid/page5

cheers

Cromamedia
6 Feb 2012, 10:01 AM
Mike, have you considered rebuilding your Xcode project from scratch? After that, add your current www folder.
A little step back in work but a big gain in stability.

mike lebowski
6 Feb 2012, 10:09 PM
good suggestion. Just tried it. Created a new phonegap app, dropped in my www ... same errors :( . Still looking for any possible dangling reference to that Header.js . Not even sure if that is the only problem with migration, but until i get past those errors, i won't know.

mike lebowski
6 Feb 2012, 10:11 PM
ok, i think i have found them. Don't know why mac searchlight search did not find them.

mike lebowski
7 Feb 2012, 12:07 AM
got app basically migrated from PR3 to beta, and phonegap 1.2 to 1.4.1 . Most things working , still dealing with some "not an object" issues, but think the worst is past. thanks

Sasha172
8 Feb 2012, 1:45 AM
iOS has a problem reading json files. There is no JSON reader. You need to explicitly call it in.

The other option (which I use) is to convert your .json files into .js files.
Keep the same data format, etc. Just add a "var x = " before the json files. Then everything works

eg. The original json file :


{"items":[
{
"day":1,
"data1":90,
"data2":60
}
]}


My all new .js file :D


var test = {"items":[
{
"day":1,
"data1":90,
"data2":60,
"data3":291,
}
]};


Now import the .js file in your index.html file and voila everything works :) No need for the Ajax request :)

Cromamedia
8 Feb 2012, 2:02 AM
Hi, Sasha172

That is good is you just need some data in a JSON format. But in a more general case, use a store filled up by a request (in this case local request) gives you a lot of automatic benefits like link to lists, sorters, filters...

For just some data is ok tho, It depends on the situation to preffer one option or the other.