The increasing focus on Digital Transformation has led to more and more use cases where organizations want their enterprise apps to be usable even when the device is offline, and later synchronize the data with the enterprise when the device comes online again.
In this IBM code pattern, we will show you how to securely implement the mobile offline synchronization use case using IBM Mobile Foundation and other IBM technologies.
When you have completed this code pattern, you will understand:
- How to achieve offline user authentication in mobile apps using JSONStore.
- How to store data securely on the device using encrypted JSONStore.
- How to achieve downstream and upstream synchronization of data between CouchDB/Cloudant database and the device using JSONStore's automated data synchronization feature.
- How to achieve downstream and upstream synchronization of images between Cloud Object Storage and the device using imgCache.js and Cordova File API.
- When there is network connectivity, user launches and logs in to the mobile app.
- The mobile app sends the user credentials to Mobile Foundation server for validation. Mobile Foundation server validates the user credentials and returns an appropriate response to the mobile app.
- If user authentication succeeds, mobile app initializes JSONStore collection with the current user credentials
- The mobile app initiates data synchronization with the Cloudant database by way of the Mobile foundation adapter.
- The Mobile Foundation sync adapter makes REST calls to the Cloudant database and returns synchronization data to the mobile app. The data that is fetched from the Cloudant database will have references to the images, which are stored on Cloud Object Storage.
- Mobile app makes a call to Mobile Foundation adapter, which makes a call to Cloud Object Storage, to get the authorization token. This token will be used by the Mobile app to access the Cloud Object Storage.
- The mobile app fetches the images using the image-caching plugin.
- On the mobile app, the synchronized data (from Cloudant) and images (from Cloud Object Storage) are downloaded and available for the user to interact with. User can view the detail page consisting of image and geo-location marked inside Google Maps.
- After the user views and updates the data in the mobile app, the mobile app stores the new data in the JSONStore collection, which automatically synchronizes the data to the Cloudant database and the images to the Cloud Object Storage by way of the Mobile Foundation adapter.
Other users who click on refresh button on the home page (and those who log in anew) are shown the updated list of problem reports.
- When the device is offline, the user launches and logs in to the mobile app.
- If the correct password is entered, the mobile app initializes the JSONStore collection with the credentials.
- If user authentication succeeds (that is, JSONStore initializes successfully), the mobile app reads data from the previously synchronized JSONStore collection and shows the list of items on the Home page.
- The user can view the detail pages for the items in the list. If the detail page was previously viewed when the device was online, the image has been cached and is viewable offline. The Cordova plugin for Google Maps ensures that the map view works in offline mode.
- The user can report new civic problems, and capture images and geo location information.
- The mobile app stores the new data in the JSONStore collection and the image and its thumbnail on local file storage on the mobile device.
- At at later time, when the device comes online, the mobile app automatically initiates the synchronization of JSONStore collection with the Cloudant database by making a call to MFP sync adapter, which posts the new data to the Cloudant database.
- The mobile app fetches the authorization token for interacting with the Cloud Object Storage service by making a call to MFP adapter, and then uploads the new images to Cloud Object Storage.
Other users who click on refresh button on the home page (and those who log in anew) can see the newly reported civic problem and its details.
- 1. Setup Ionic and MFP CLI
- 2. Create Cloudant database and populate it with sample data
- 3. Create IBM Cloud Object Storage service and populate it with sample data
- 4. Create Mobile Foundation service and configure MFP CLI
- 5. Download source repo and customize
- 6. Deploy the MFP Adapters and Test them
- 7. Run application on Android phone
- 8. Test the app functionality in offline mode
This project builds on top of https://github.com/IBM/Ionic-MFP-App. Run following steps from that base project to provision the needed mobile backend services from IBM Cloud and populate them with sample data, as well as to setup Ionic and MFP CLI on your development machine.
- Step 1. Setup Ionic and MFP CLI
- Step 2. Create Cloudant database and populate it with sample data
- Step 3. Create IBM Cloud Object Storage service and populate it with sample data
- Step 4. Create Mobile Foundation service and configure MFP CLI
$ git clone https://github.com/IBM/MFP-JSONStore-OfflineSync
$ cd MFP-JSONStore-OfflineSync
Update IonicMobileApp/config.xml
as below. Change id
, name
, description
and author
details appropriately.
<?xml version='1.0' encoding='utf-8'?>
<widget id="org.mycity.myward" version="2.0.0" xmlns="https://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0" xmlns:mfp="http://www.ibm.com/mobilefirst/cordova-plugin-mfp">
<name>MyWard</name>
<description>Get your civic issues resolved by posting through this app.</description>
<author email="shivahr@gmail.com" href="https://developer.ibm.com/code/author/shivahr/">Shiva Kumar H R</author>
...
Open MobileFoundationAdapters/JSONStoreCloudantSync/src/main/adapter-resources/adapter.xml
and update the following properties to point to the Cloudant database created in Step 2.
- Update
username
andpassword
with the Cloudant API key as generated in Step 2.2. - For property
host
, specify the Cloudant Dashboard URL portion after (and excluding) https:// and upto (and including) -bluemix.cloudant.com as shown in the snapshot of Step 2.2. - For property
protocol
, leave the default value ofhttps
as-is. - For property
port
, leave the default value of443
as-is. - For property
dbname
, leave the default value ofmyward
as-is. - For property
createnewdbifnotexist
, leave the default value offalse
as-is.
<mfp:adapter name="JSONStoreCloudantSync" ...>
<property name="username" displayName="Cloudant Username" defaultValue=""/>
<property name="password" displayName="Cloudant Password" defaultValue=""/>
<property name="host" displayName="Cloudant Host" defaultValue=""/>
<property name="protocol" displayName="DB protocol" defaultValue="https" />
<property name="port" displayName="Db port" defaultValue="443" />
<property name="dbname" displayName="Cloudant Database Name" defaultValue="myward"/>
<property name="createnewdbifnotexist" displayName="Create database if it does not exist?" defaultValue="false" />
...
</mfp:adapter>
Open MobileFoundationAdapters/MyWardData/src/main/adapter-resources/adapter.xml
and update the following properties to point to the Cloud Object Storage created in Step 3.
- Specify value for
bucketName
as created in Step 3.1. - Specify
serviceId
andapiKey
created in Step 3.2. - While creating the bucket in Step 3.1, if you selected a different Location/Resiliency, then update the
endpointURL
as per the specification in https://cloud.ibm.com/docs/services/cloud-object-storage/basics/endpoints.html#select-regions-and-endpoints.
<mfp:adapter name="MyWardData" ...>
...
<property name="endpointURL" displayName="Cloud Object Storage Endpoint Public URL" defaultValue="https://s3-api.us-geo.objectstorage.softlayer.net"/>
<property name="bucketName" displayName="Cloud Object Storage Bucket Name" defaultValue=""/>
<property name="serviceId" displayName="Cloud Object Storage Service ID" defaultValue="" />
<property name="apiKey" displayName="Cloud Object Storage API Key" defaultValue=""/>
</mfp:adapter>
Build and deploy UserLogin
Adapter as below.
$ cd MobileFoundationAdapters/
$ cd UserLogin
$ mfpdev adapter build
$ mfpdev adapter deploy
Note: In Step 4, if you specified No
to Make this server the default?
, then you need to specify the name of your server profile (Cloud-MFP
in our case) at the end of mfpdev adapter deploy
command as shown below.
$ mfpdev adapter deploy Cloud-MFP
Build and deploy MyWardData
adapter as below.
$ cd ../MyWardData/
$ mfpdev adapter build
$ mfpdev adapter deploy
Build and deploy JSONStoreCloudantSync
adapter as below.
$ cd ../JSONStoreCloudantSync/
$ mfpdev adapter build
$ mfpdev adapter deploy
Launch MFP Dashboard as below:
- In the IBM Cloud dashboard, under
Cloud Foundry Services
, click on theMobile Foundation
service you created in Step 4. The service overview page that gets shown, will have the MFP dashboard embedded within it. You can also open the MFP dashboard in a separate browser tab by appending/mfpconsole
to the url mentioned in Step 4. - Inside the MFP dashboard, in the list on the left, you will see the
JSONStoreCloudantSync
,UserLogin
andMyWardData
adapters listed.
Verify MFP Adapter configuration as below:
- Inside the MFP dashboard, click on the
JSONStoreCloudantSync
adapter. UnderConfigurations
tab, you should see the various properties we specified in Step 5.3 for accessing Cloudant database as shown below. As an alternative to specifying those property values inMobileFoundationAdapters/JSONStoreCloudantSync/src/main/adapter-resources/adapter.xml
as previously shown in Step 5.3, you can deploy the adapters with emptydefaultValue
, and once the adapter is deployed, change the values on this page.
- Click on
Resources
tab. You should see the various REST APIs exposed byJSONStoreCloudantSync
adapter as shown below. TheSecurity
column should show the protecting scopeUserLogin
against each REST method.
Create temporary credentials to test adapter REST API as below:
- Inside the MFP dashboard, click on
Runtime Settings
. Click onConfidential Clients
. Then click onNew
. - In the form that pops up, specify values for
ID
andSecret
as shown in snapshot below. ForAllowed Scope
enter**
and click onAdd
. Finally click onSave
.
Test adapter REST API as below:
- Inside the MFP dashboard, click on the
JSONStoreCloudantSync
adapter. Click onResources
and then click onView Swagger Docs
. The Swagger UI for adapter REST APIs will get shown in a new window/tab. - Inside the Swagger UI, click on
Expand Operations
. - To test the
POST /modifications
API, first click onOFF
toggle button to enable authentication. SelectUserLogin
and click onAuthorize
as shown below. Enter theID
andSecret
created above againstUsername
andPassword
. ClickOK
. If authentication is successful, the toggle button will switch toON
position.
- Specify value for parametrer
db_name
asmyward
, for parameterseqid
as0
, and following JSON object under POST body:
{
"_id": "string",
"_rev": "string",
"json": "[]"
}
- Finally click on
Try it out
button to run thePOST /modifications
API. The API response should get shown in theResponse Body
as shown in snapshot below.
Delete the temporary credentials after testing adapter REST API as below:
- Inside the MFP dashboard, click on
Runtime Settings
. Click onConfidential Clients
. - Delete the
Client ID
created previously.
Follow the instructions in Step 7 of base project to run the application on Android phone.
A note on how the offline mode is supported for each of the pages* in MyWard app: (*Ionic page is the equivalent of iOS View or Android Activity)
-
Login
page:- The first time the app is launched after installation, the user login will work only in online mode. This is to make sure that the user credentials are validated by making a call to MFP security adapter. Once the user authentication succeeds, subsequent login attempts by the same user are supported in offline mode with the help of encrypted JSONStore.
-
Home
page: (downstream sync)- JSONStore for storing/syncing data with Cloudant.
- imgcache.js for caching the image thumbnails loaded from Cloud Object Storage.
-
Problem Detail
page: (downstream sync)Grievances for which the details are already seen in online mode:
- JSONStore for storing/syncing data with Cloudant.
- imgcache.js for caching the image loaded from Cloud Object Storage.
- Cordova plugin for Google Maps to make sure that the map view works even in offline mode.
-
Report New Problem
page: (upstream sync)- Data to be uploaded to Cloudant is stored in JSONStore which later syncs it with Cloudant when device comes online.
- Image and its thumbnail are stored on local storage, and are later uploaded to Cloud Object Storage when devices comes online.
Note: In the current implementation, images and its thumbnails are stored as-is on the local file storage at cordova.file.dataDirectory which is private to the application. If you have a more stringent compliance requirement of having to encrypt the images stored locally on the phone, then follow the recommendations on this page.
- Build and run the application on your phone as per instructions in Step 7.6.
- Login with username say
Test
and passwordTest
. (Note: We have used a simple MFP security adapter that returns success when password equals username.) - Make sure that the
Home
page displays list of grievances along with image thumbnails. - Click on a few of the grievances to see their details. On the
Problem Detail
page, make sure that the image and Google Maps location are displayed. - Back on the
Home
page, click on the+
icon to report a new grievance. Add description, take photo, grab geolocation and finally submit. Make sure that the new grievance is successfully submitted to server. - On a different device, whoever launches the app (or clicks on refresh button) should see your newly reported grievance.
- Have the device go offline by turning off
Mobile data
andWi-Fi
. - Launch the
MyWard
app. - Login using the same username and password as before. Make sure login succeeds even in offline mode.
- Make sure that the
Home
page displays list of grievances along with image thumbnails. - Click on the grievances for which you had seen the details before. Make sure that the image and Google Maps location are displayed even in offline mode.
- Back on the
Home
page, click on the+
icon to report a new grievance. Add description, take photo, grab gelocation and finally submit. Make sure that the new grievance report is successfully accepted even in offline mode. - Back on the
Home
page, the newly reported problem should get listed at the end along with its thumbnail. Upon clicking it, theProblem Detail
page should show image. - Get the device online by turning on either
Mobile data
orWi-Fi
. - In a while the newly reported grievance would get uploaded to server and page would get refreshed to show new list of grievances as seen on server. Make sure that the grievance that you just reported is listed as well.
- On a different device, whoever launches the app (or clicks on refresh button) should see your newly reported grievance.
Please see instructions for debugging Android hybrid app using Chrome Developer Tools or troubleshooting guide for solutions to some commonly occuring problems.
In case the JSONStore data on the phone gets messed up during app development/testing, then you can clear it as per the instructions below.
-
Install Google Chrome.
-
Open Google Chrome. Open URL
chrome://inspect/#devices
-
Under
Devices
, click onInspect
below your connected device. -
Click on
Console
view. -
In the command prompt at the end, type following command and press enter. This would delete all the JSONStore collections on the device.
WL.JSONStore.destroy();
- JSONStore - Encrypted on device storage & automated data sync for better app performance & offline access
- Offline authentication using JSONStore
- Automated synchronization of JSONStore collections with CouchDB databases
- cordova-plugin-file - a File API allowing read/write access to files residing on the device
This code pattern is licensed under the Apache Software License, Version 2. Separate third party code objects invoked within this code pattern are licensed by their respective providers pursuant to their own separate licenses. Contributions are subject to the Developer Certificate of Origin, Version 1.1 (DCO) and the Apache Software License, Version 2.