<p dir="ltr">If you are building mobile apps today, and you have somewhere in your code an alert box with the following message “Please check your internet connection” exactly at the moment when a user wants to use your app, please read on.</p>
The chances are that the user is already aware that he doesn’t have internet connection, and he needs to do something with your app! I am glad to announce that with the new Telerik Platform support for offline apps you can now do full CRUD operations on top of your data locally, and synchronize everything with a single method when your connection is back.<h1>It's all about user experience</h1> <p><img src="//d585tldpucybw.cloudfront.net/sfimages/default-source/default-album/telerik-backend-services-offline.png?sfvrsn=0" displaymode="Original" alt="Telerik Backend Services Offline" title="Telerik Backend Services Offline" /></p> <p dir="ltr">Presenting your users with full capabilities means that you have to do a lot under the cover. You will need a full-blown support for persisting the information at the device, preferably directly on the device file system instead of the browser local storage to avoid storage limits. You will also need to abstract your service calls that require internet connection to a layer that talks to the file system, and lastly take care of synchronization of your data to the server when the connection is re-established.</p> <p dir="ltr">It’s no wonder that my session for Building Offline Ready Mobile Apps was fully packed with people standing and sitting on the floor (sorry about that :)). Why, because building offline apps involves two of the biggest pitfalls in software development. First, this is <strong>a requirement your users expect to have, but never explicitly ask for it</strong>, because they assume it will be there. WRONG! And second, it’s not the type of features that you can easily add if you haven’t designed the app for it early on.</p> <div>Let’s see how we have made this much easier with our JavaScript SDK along with all supported capabilities.<br /> <br /> </div> <h3>Full support for Hybrid, Web and NativeScript</h3> <div>Using a single JavaScript library and the same API, you can build both Hybrid, Web and NativeScript apps. Simply integrate our JavaScript SDK in your Hybrid or NativeScript app, and you are ready to roll.<br /> <br /> <div class="reCodeBlock" style="border: 1px solid #7f9db9; overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important;"><code style="color: #000000;"><a href="https://bs-static.cdn.telerik.com/1.3.1/everlive.all.min.js">https://bs-static.cdn.telerik.com/1.3.1/everlive.all.min.js</a></code></span></div> </div> </div> <h3>Global setting for Online/Offline mode</h3> <div>You have a global setting for the JS SDK to either perform all operations against the server (online) or against the local file storage (offline). You can make full create, delete and update operations on items that have been already retrieved at the device, and read operations will full query support at any item that has been already retrieved on the device.<br /> <br /> <div class="reCodeBlock" style="border: 1px solid #7f9db9; overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important;"><code style="color: #008200;">//Assume that you have the Everlive instance as a global variable</code></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important;"><code style="color: #006699; font-weight: bold;">var</code> <code style="color: #000000;">el = </code><code style="color: #006699; font-weight: bold;">new</code> <code style="color: #000000;">Everlive({</code></span></div> <div style="background-color: #ffffff;"><span><code> </code><span style="margin-left: 12px !important;"><code style="color: #000000;">apiKey: </code><code style="color: blue;">'your-api-key-here'</code><code style="color: #000000;">,</code></span></span></div> <div style="background-color: #f8f8f8;"><span><code> </code><span style="margin-left: 12px !important;"><code style="color: #000000;">offlineStorage: </code><code style="color: #006699; font-weight: bold;">true</code></span></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important;"><code style="color: #000000;">});</code></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important;"><code style="color: #008200;">//Switch to online mode</code></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important;"><code style="color: #000000;">el.online();</code></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important;"><code style="color: #008200;">//Switch to offline mode</code></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important;"><code style="color: #000000;">el.offline();</code></span></div> </div> </div> <h3>Per request level setting for Online/Offline mode</h3> <div>You can also decide to handle specific requests differently. For example, you can make a read request of a type that you know is not changing often directly from the local storage if it has been previously retrieved. This will save you many roundtrips to the server. We will be soon introducing caching capabilities with expiration times that will make such scenarios even easier!<div class="reCodeBlock" style="border: 1px solid #7f9db9; overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important;"><code style="color: #006699; font-weight: bold;">var</code> <code style="color: #000000;">el = </code><code style="color: #006699; font-weight: bold;">new</code> <code style="color: #000000;">Everlive({</code></span></div> <div style="background-color: #f8f8f8;"><span><code> </code><span style="margin-left: 12px !important;"><code style="color: #000000;">apiKey: </code><code style="color: blue;">'your-api-key-here'</code><code style="color: #000000;">,</code></span></span></div> <div style="background-color: #ffffff;"><span><code> </code><span style="margin-left: 12px !important;"><code style="color: #000000;">offlineStorage: </code><code style="color: #006699; font-weight: bold;">true</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important;"><code style="color: #000000;">});</code></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important;"> </span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important;"><code style="color: #000000;">el.data(</code><code style="color: blue;">'MyContentType'</code><code style="color: #000000;">).useOffline(</code><code style="color: #006699; font-weight: bold;">true</code><code style="color: #000000;">).getById(...);</code></span></div> </div> <h3>Support for multiple offline storage providers</h3> <div> <p dir="ltr">When you enable the offline mode, the SDK needs to store the data locally. If you are in the browser or in a Hybrid app relying on Cordova, you can always use the Browser’s local storage. However, it has just 5 MBs limitation. That is why, we made it possible to easily configure the storage provider to actually use the file system if you are in a Hybrid or NativeScript app. That way, using the file system your app has unlimited access to the device storage.</p> <div> <div class="reCodeBlock" style="border: 1px solid #7f9db9; overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important;"><code style="color: #006699; font-weight: bold;">var</code> <code style="color: #000000;">el = </code><code style="color: #006699; font-weight: bold;">new</code> <code style="color: #000000;">Everlive({</code></span></div> <div style="background-color: #f8f8f8;"><span><code> </code><span style="margin-left: 12px !important;"><code style="color: #000000;">apiKey: </code><code style="color: blue;">'your-api-key-here'</code><code style="color: #000000;">,</code></span></span></div> <div style="background-color: #ffffff;"><span><code> </code><span style="margin-left: 12px !important;"><code style="color: #000000;">offlineStorage: {</code></span></span></div> <div style="background-color: #f8f8f8;"><span><code> </code><span style="margin-left: 24px !important;"><code style="color: #000000;">storage: {</code></span></span></div> <div style="background-color: #ffffff;"><span><code> </code><span style="margin-left: 36px !important;"><code style="color: #008200;">//provider: Everlive.Constants.StorageProvider.LocalStorage</code></span></span></div> <div style="background-color: #f8f8f8;"><span><code> </code><span style="margin-left: 30px !important;"><code style="color: #000000;">provider: Everlive.Constants.StorageProvider.FileSystem</code></span></span></div> <div style="background-color: #ffffff;"><span><code> </code><span style="margin-left: 24px !important;"><code style="color: #000000;">}</code></span></span></div> <div style="background-color: #f8f8f8;"><span><code> </code><span style="margin-left: 12px !important;"><code style="color: #000000;">}</code></span></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important;"><code style="color: #000000;">});</code></span></div> </div> </div>
Note that if you choose to use the FileSystem in Cordova app, you should also enable the File plug-in, since it’s a dependency.<br /> <br /> </div> <h3>Data synchronization with the server</h3> <div>Once you enable offline mode, all your changes to the items are persisted locally. You can easily synchronize this changes with the server when your app is already online. Note that we rely on you to tell the SDK when it’s online/offline and when to synchronize the data in order to leave this behavior up to you and your UI scenarios.<br /> <br /> <div class="reCodeBlock" style="border: 1px solid #7f9db9; overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important;"><code style="color: #006699; font-weight: bold;">var</code> <code style="color: #000000;">el = </code><code style="color: #006699; font-weight: bold;">new</code> <code style="color: #000000;">Everlive({</code></span></div> <div style="background-color: #f8f8f8;"><span><code> </code><span style="margin-left: 12px !important;"><code style="color: #000000;">apiKey: </code><code style="color: blue;">'your-api-key-here'</code><code style="color: #000000;">,</code></span></span></div> <div style="background-color: #ffffff;"><span><code> </code><span style="margin-left: 12px !important;"><code style="color: #000000;">offlineStorage: </code><code style="color: #006699; font-weight: bold;">true</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important;"><code style="color: #000000;">});</code></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important;"> </span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important;"><code style="color: #000000;">el.sync();</code></span></div> </div> </div> <h3>Conflict resolution with three built in policies<br /> <br /> </h3> <p>When data is synchronized with the server, it’s imminent that conflicts may arise. Since you are dealing with a highly distributed systems where multiple devices might interact with the same data items, you should be prepared to handle this. We have created three built-in policies that you can use in the following way:</p> <h4>Client-wins strategy - overrides changes on the server with the most recent client data</h4> <p></p> <p> <div class="reCodeBlock" style="border: 1px solid #7f9db9; overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important;"><code style="color: #006699; font-weight: bold;">var</code> <code style="color: #000000;">el = </code><code style="color: #006699; font-weight: bold;">new</code> <code style="color: #000000;">Everlive({</code></span></div> <div style="background-color: #f8f8f8;"><span><code> </code><span style="margin-left: 12px !important;"><code style="color: #000000;">apiKey: </code><code style="color: blue;">'your-api-key-here'</code><code style="color: #000000;">,</code></span></span></div> <div style="background-color: #ffffff;"><span><code> </code><span style="margin-left: 12px !important;"><code style="color: #000000;">offlineStorage: {</code></span></span></div> <div style="background-color: #f8f8f8;"><span><code> </code><span style="margin-left: 24px !important;"><code style="color: #000000;">conflicts: {</code></span></span></div> <div style="background-color: #ffffff;"><span><code> </code><span style="margin-left: 36px !important;"><code style="color: #000000;">strategy: Everlive.Constants.ConflictResolutionStrategy.ClientWins</code></span></span></div> <div style="background-color: #f8f8f8;"><span><code> </code><span style="margin-left: 24px !important;"><code style="color: #000000;">}</code></span></span></div> <div style="background-color: #ffffff;"><span><code> </code><span style="margin-left: 12px !important;"><code style="color: #000000;">}</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important;"><code style="color: #000000;">});</code></span></div> </div> </p> <h4>Server-wins streategy- overrides the changes of the client with the most recent server data</h4> <p> <div class="reCodeBlock" style="border: 1px solid #7f9db9; overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important;"><code style="color: #006699; font-weight: bold;">var</code> <code style="color: #000000;">el = </code><code style="color: #006699; font-weight: bold;">new</code> <code style="color: #000000;">Everlive({</code></span></div> <div style="background-color: #f8f8f8;"><span><code> </code><span style="margin-left: 12px !important;"><code style="color: #000000;">apiKey: </code><code style="color: blue;">'your-api-key-here'</code><code style="color: #000000;">,</code></span></span></div> <div style="background-color: #ffffff;"><span><code> </code><span style="margin-left: 12px !important;"><code style="color: #000000;">offlineStorage: {</code></span></span></div> <div style="background-color: #f8f8f8;"><span><code> </code><span style="margin-left: 24px !important;"><code style="color: #000000;">conflicts: {</code></span></span></div> <div style="background-color: #ffffff;"><span><code> </code><span style="margin-left: 36px !important;"><code style="color: #000000;">strategy: Everlive.Constants.ConflictResolutionStrategy.ServerWins</code></span></span></div> <div style="background-color: #f8f8f8;"><span><code> </code><span style="margin-left: 24px !important;"><code style="color: #000000;">}</code></span></span></div> <div style="background-color: #ffffff;"><span><code> </code><span style="margin-left: 12px !important;"><code style="color: #000000;">}</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important;"><code style="color: #000000;">});</code></span></div> </div> </p> <h4>Custom - implement your own client side function to handle the resolution based on custom logic</h4> <p> <div class="reCodeBlock" style="border: 1px solid #7f9db9; overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important;"><code style="color: #006699; font-weight: bold;">var</code> <code style="color: #000000;">el = </code><code style="color: #006699; font-weight: bold;">new</code> <code style="color: #000000;">Everlive({</code></span></div> <div style="background-color: #f8f8f8;"><span><code> </code><span style="margin-left: 12px !important;"><code style="color: #000000;">apiKey: </code><code style="color: blue;">'your-api-key-here'</code><code style="color: #000000;">,</code></span></span></div> <div style="background-color: #ffffff;"><span><code> </code><span style="margin-left: 12px !important;"><code style="color: #000000;">offlineStorage: {</code></span></span></div> <div style="background-color: #f8f8f8;"><span><code> </code><span style="margin-left: 24px !important;"><code style="color: #000000;">conflicts: {</code></span></span></div> <div style="background-color: #ffffff;"><span><code> </code><span style="margin-left: 36px !important;"><code style="color: #000000;">strategy: Everlive.Constants.ConflictResolutionStrategy.Custom,</code></span></span></div> <div style="background-color: #f8f8f8;"><span><code> </code><span style="margin-left: 36px !important;"><code style="color: #000000;">implementation: </code><code style="color: #006699; font-weight: bold;">function</code><code style="color: #000000;">(conflicts) { ... }</code></span></span></div> <div style="background-color: #ffffff;"><span><code> </code><span style="margin-left: 24px !important;"><code style="color: #000000;">}</code></span></span></div> <div style="background-color: #f8f8f8;"><span><code> </code><span style="margin-left: 12px !important;"><code style="color: #000000;">}</code></span></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important;"><code style="color: #000000;">});</code></span></div> </div> </p> <h3>Offline data encryption</h3> <p>By default, the data you store at the device is stored as plain text. In case the device is lost or stolen this data can be retrieved. We have accounted for this scenario, and you have capabilities to encrypt any data that is stored locally using an encryption key and Advanced Encryption Standard (AES) implementation built-in in the SDK.<br /> <br /> <div class="reCodeBlock" style="border: 1px solid #7f9db9; overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important;"><code style="color: #006699; font-weight: bold;">var</code> <code style="color: #000000;">el = </code><code style="color: #006699; font-weight: bold;">new</code> <code style="color: #000000;">Everlive({</code></span></div> <div style="background-color: #f8f8f8;"><span><code> </code><span style="margin-left: 12px !important;"><code style="color: #000000;">apiKey: </code><code style="color: blue;">'your-api-key-here'</code><code style="color: #000000;">,</code></span></span></div> <div style="background-color: #ffffff;"><span><code> </code><span style="margin-left: 12px !important;"><code style="color: #000000;">offlineStorage: {</code></span></span></div> <div style="background-color: #f8f8f8;"><span><code> </code><span style="margin-left: 24px !important;"><code style="color: #000000;">encryption: {</code></span></span></div> <div style="background-color: #ffffff;"><span><code> </code><span style="margin-left: 36px !important;"><code style="color: #000000;">key: </code><code style="color: blue;">'your-encryption_key_here'</code></span></span></div> <div style="background-color: #f8f8f8;"><span><code> </code><span style="margin-left: 24px !important;"><code style="color: #000000;">}</code></span></span></div> <div style="background-color: #ffffff;"><span><code> </code><span style="margin-left: 12px !important;"><code style="color: #000000;">}</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important;"><code style="color: #000000;">});</code></span></div> </div> </p> <h3>Kendo UI DataSource integration</h3> <p>We have made it possible for customers who are using our Backend Services together with a Kendo UI DataSource to feel at home. However, since both Kendo UI and the JS SDK of the Backend Services support offline, you should first understand the difference to decide which one to use. We have a dedicated article in our documentation to help you make most of the integration between the two products <a href="http://docs.telerik.com/platform/backend-services/development/javascript-sdk/kendoui/kendo-offline-mode">here</a>.</p> <h3>Data Connectors and offline</h3> <p dir="ltr">This is my favorite feature, because you can easily connect to your own SQL Server, Postgre SQL, MySQL Enterprise and Oracle database, and directly use all of the features above without any limitation. The beauty is that we are supporting absolutely the same API layer when working with existing data or data stored in the cloud in our MongoDB infrastructure. This gives you a nice abstraction at the client, so you don’t have to care from where the data is coming from.</p> <p>We cannot wait to see all of the nice apps you create using this new capability. If you have any issues or questions, get back with us and we will be able to help you integrate the Offline capabilities in your existing apps powered by the Telerik Platform.</p> </div><img src="http://feedpress.me/10810/1235060.gif" height="1" width="1"/>
↧