A common requirement in Sencha Touch is that an application must continue working normally when a device loses its connection to the Internet. Sencha Cmd delivers some great tools that give us everything we need for the app to work offline, such as auto generated App Manifest files, but one of the biggest issues is how to handle your data. There are numerous ways to handle data, and one common technique is to switch proxies between a local storage proxy and an AJAX proxy. In this post, we’ll show you how you can achieve the same effect, but we’ll use a single proxy and make it completely transparent to the programmer using the store configured with it.
In this example, we’re going to extend the AJAX proxy; you could extend whichever proxy you like, but as AJAX is quite a common requirement, we’ll use that one. We only need to override two methods that deal with the data flowing into the proxy. We’re also going to create a few config items, which we’ll explain later. Here’s the bare bones class that we’re going to create without any of the logic in place yet:
doRequest() actually performs the request to the server. We override this method because we want to intercept whether the device is offline and/or has no access to the server and essentially fake the response back to the store.
processResponse() interprets the response from the server. The main reason we override this method is because, while we still want all of the original functionality, we want to store the data we get in a successful response to our storage facility. In the case of an unsuccessful request, we want to tell our proxy to do it again but with our faked response as described above.
The proxy is going to use a storage facility being passed to it. This is simply a singleton class with two methods on it: getItem and setItem. You can use any storage facility and as long as you implement an API in it in the same way as we’re doing here, it will work:
Nothing too remarkable here, but you should note that the setItem and getItem methods allow success and failure callbacks to be supplied. Also, in the constructor, we set up the SQL database; in a more straightforward case like local storage, this step wouldn’t be necessary.
Digging a bit deeper into how this works, let’s look at setItem:
Here we are taking the key we want to set (the storage key comes from the proxy), the new value (in this case a serialized JSON object) and an object containing callbacks as parameters. We remove any old references for this key and then insert our new value.
are the equivalent of this code, using local storage:
If we have success in this transaction, we call the success callback passed in, otherwise we call the error callback.
getItem works very similarly:
This time we only have two parameters, key and callbacks. The key is used to retrieve the item in question and then we call the success callback with that value if we can find it. Otherwise, we call the error callback.
The Final Proxy
Now that we have our storage facility, we can complete our proxy. The proxy will make use of our storage facility and will be passed in when the proxy is configured.
The first method we’re going to override is doRequest(). In the original AJAX class, this method is used to perform the actual request to the server, and in the case where the device is online, we will simply call into the parent method using callParent(); If, however, the device is offline then we will essentially fake the response by getting the data from our offline storage facility and callingprocessResponse(). We have to fake this response because processResponse() will interrogate what’s passed to it to make sure it’s a valid response. We fake it by setting the correct http status code (200), setting the responseText to the data we withdrew from our storage facility and set astatusText of “OK”. This object now looks to the processResponse method like a completely normal request response. This kind of abstraction is something the Sencha framework is very good at and makes for nicely decoupled code.
The second method we override is processResponse(). We, again, in normal circumstancescallParent() when there is a successful request to the server, but additionally we will save the data from the request into the offline storage facility.
There are a few stages in this process. Firstly, if the request success flag is true (i.e. we just got a valid response from the server), we make a check to the config item “online” that is set on the proxy. This is a value that can be passed into the proxy on initialize. Alternatively, the proxy will default to this being true until such time as a request fails, and it will then assume the device is offline. If the flag is set to true and we have our storage facility passed in, then we will store the data we just received into it. We do this every time a successful request is made, so that if the device goes offline it always has the latest data that it had access to at the time.
If the request failed, we set the online flag to false and we re-run the doRequest method which, now that the online flag set to false, will go and fetch the data from our storage facility.
Bringing It all Together
All of these elements get pulled together when we configure the proxy on our store:
As you can see, we set the type to “offline” as our proxy’s alias is set to proxy.offline. ThestorageKey is the key in the offline data storage you want to store the data for the request against. In this case, the store is called “buttons”, so we gave the storage key the same name. The storage facility is the class we created above and everything else is what you’d expect in a standard proxy configuration.
We’ve developed a demo Sencha Touch app for you to see all of this code in action. In addition, here are some screenshots to demonstrate. This demo app has a toolbar, and its content is driven by a JSON file on the server.
In the first image, you can see that the buttons have been generated, and in the console, you can observe that the data has been stored to the offline storage.
In the second image, the test-resource.json file is no longer available. In this case, I simply renamed it so we got a 404 response (though this could have been because the device could no longer access the internet or the server was down etc). You can see from the console logs that instead we simply load from the offline version, and the buttons are successfully loaded.
The flexibility of the Sencha class system means it’s easy to extend and repurpose built-in components and utilities. As we have demonstrated, a potentially difficult problem is easily solved by hooking into an already well-defined workflow and simply adding the additional functionality we need. The result is that we have retained the power of the original proxy while making the chore of managing offline data completely transparently to the developer using it.