Skip to Content
Technical Articles
Author's profile photo Ludo Noens

Creating an Offline CRUD hybrid mobile app in SAP Web IDE Full-Stack with Hybrid Application Toolkit

Updated 3 March 2021: added steps to avoid CORS issues on iOS

Updated 5 January 2021: updated as the Neo trial landscape is no longer available

Updated 24 March 2020: added information regarding multiple offline stores

Updated 21 February 2020: as the old Mobile Services sample OData service has been deprecated, I’ve updated the destination configuration.

 

Important note: although hybrid apps are a valid way to implement offline apps, we strongly recommend using more modern mobile technologies instead. 

When you start developing a new mobile app, we strongly recommend that you consider developing this with either MDK (Mobile Development Kit) for cross platform applications, or our native SDKs (SAP Cloud Platform SDK for iOS or SAP Cloud Platform SDK for Android).

 

A new milestone and a bit of background

As a Product Owner, it is always good to see your product reach a new milestone. Last week, you’ve seen the announcement of Hybrid Application Toolkit release 1805 in Britt’s blog. In this release, we’ve made some fundamental changes in the way we build hybrid apps in the cloud from SAP Web IDE.

It is not my intention to repeat Britt’s blog; but let me just highlight the most important part of this release: Developers have a lot more control on what is packaged into a hybrid app. This opens the door to more possibilities and allows developers to more easily resolve challenges by themselves.

One of the hot topics we did not completely cover in this release is the ability to create offline apps. Many developers have been asking for this. In December 2017, I’ve announced our plans to ‘end-of-maintenance’ the HAT local add-on. We have provided you one offline specific template in SAP Web IDE (with that I mean the Eclipse Orion based version) that could only be used in conjunction with the HAT local add-on. The template is not available in SAP Web IDE Full-Stack. Meanwhile, our colleagues from SAP Web IDE have shut down the old SAP Web IDE and you are advised to migrate projects to the Full-Stack version. So how do you build offline hybrid apps in SAP Web IDE Full-Stack, if we don’t provide the template anymore?

Before answering, let’s look back into the history of this toolkit. When we started, we had no cloud build feature. Instead, we were relying on the locally installed tools for developing Cordova apps. There were templates in Web IDE that kept evolving over time. At some point we’ve decided to grab a snapshot of a UI5 Master Detail template and adapt it to support offline. This template was shipped with HAT, the Web IDE plugin. We then tried to keep up with changes in the original template. At some point, the original template was replaced with a Fiori Master-Detail Application template. We did not further update our offline template, unless really needed.

Although we could have created new mobile specific templates in HAT, we actually wanted the opposite: any template should by default support mobile; hence there should not be mobile variants at all. The functionality we’ve provided last year was actually doing this. When the HAT plugin in Web IDE was active, a newly created project was automatically enabled for mobile. We were adding some magic in the background to make this happen. However, although we’ve tried to make it easier, in reality things got a lot more difficult (especially troubleshooting).

We do not plan to provide you an offline template in SAP Web IDE Full-Stack. The main reason is that every app is unique and requires specific customization around the topic of offline. It is very difficult to provide you this as part of a one-fits-all template. Instead, we will give you guidelines on how to implement offline functionality in your app and provide you with an example below.

Creating an offline app based on the Fiori CRUD Master-Detail template

Prerequisites

Before we start creating the example app, let us first ensure we can access the Mobile Service Sample Service from SAP Web IDE. In your SAP Cloud Platform cockpit, go to Connectivity > Destinations and create a new destination as shown below. Make sure you add the additional properties as indicated. The url is not completely visible is the screenshot. Please enter https://hcpms-<your account number>trial.hanatrial.ondemand.com/mobileservices/origin/hcpms/ESPM.svc/v2. I was using a trial account for this blog post. However, the Neo trial landscape is no longer available. Please adapt the url for your productive environment.

After saving the destination settings, it can take up to a few minutes to have the new destination active.

Update 21/02/2020: for Authentication, please use App2AppSSO.

Now let’s start creating the app from scratch in SAP Web IDE Full-Stack. You can find this version of SAP Web IDE in your SAP Cloud Platform cockpit’s services list:

Although not required at this point, please make sure you have activated Hybrid Application Toolkit. Once you are in SAP Web IDE Full-Stack, go to the preferences and ensure the feature Hybrid App Toolkit is activated. Don’t forget to save your selection and reload SAP Web IDE.

You will use the Cloud Build feature based on Mobile Services. Customers who have access a productive environment could see the following in the Hybrid Application Toolkit preferences (depending on your subscription):

If you don’t see the Cloud Build Service mentioned in the preference, you will be using Mobile Services by default. If you do see the radio buttons, please ensure the radio button regarding Cloud Build Service is enabled for Mobile Services (not the deprecated one for SAP Fiori).

Creating a new project from a template

In SAP Web IDE, please select File > New > Project from Template. To avoid possible issues with the latest innovation version, I have selected a CRUD Master-Detail template based on SAPUI5 version 1.52. Feel free to use a different version.

In the next step, fill in the details.

In this example I am using the Mobile Service Sample Service to consume OData from a Service URL. We have exposed this service to SAP Web IDE through the destination we have created earlier.

In the Template Customization step, I have selected the following:

Once finished, you can run the application from SAP Web IDE in your web browser. It will show you the data from the data source (read) and you can create new entries (create), change it (update) and remove entries (delete). Also referred to as “CRUD”.

Mobile enable the project

Right-click on your project to bring up the context menu and select Mobile > Enable as Hybrid Mobile Project.

This step will add a ‘mobile’ folder to your project, containing some source files only relevant for when your app runs on a device or emulator (as Packaged app, or Developer Companion app).

Take note that during the cloud build process, the contents of the webapp folder and the mobile folder will be merged into the www folder of your Cordova project. The index.html file in the mobile folder will replace the one in your webapp project (if your project has one).

We’ve decided to use this approach, so you can still deploy the webapp to SAP Cloud Platform.

Creating a hybrid app from this project

You can create a Packaged app based on this project by selecting in the context menu: Mobile > Build Packaged App.

In the first step of the build wizard, you can change the app’s name as visible on your device’s home page. You can also select an app icon and images for the splash screen.

In the next step, you indicate for which platform you want to build your app. You’ll need to provide a valid the signing profile. For Android devices, you can simply generate one on our service. The provisioning profile for iOS is a bit more tricky. I recommend creating a wildcard App ID on developer.apple.com and include that in the profile which allows you to install the application on your device. Note that this is wildcard App ID is only for development; not for production. You’ll then need to export your certificate and provisioning profile and import it here.

Currently, we only have two UI5 versions to select from. We will gradually add more versions.

At this point you can hit the Build button. Once the Cloud Build Service is finished generating your app, a QR code and hyperlink will be displayed, so you can download the app onto your device.

During this build process, Hybrid Application Toolkit will automatically create a new application on Mobile Service, and also create the related Cloud Platform destination for you.

Adding Cordova plugins (optional)

Although not related to the offline topic, I have received several queries on how to add Cordova plugins. So let’s quickly discuss this.

In case you want to make use of one or more of the more than 2300 publicly available Cordova plugins in your project, open the context menu and choose Mobile > Select Cordova plugins. You can browse and search for plugins. Once you are done, make sure to save your selection.

When you trigger a new build, the plugins will automatically be added in your Cordova app.

Note that there is a tab called “Default (34)”, which does not allow you to make a selection. It merely lists down the plugins we add to your app by default. You’ll notice most of the Kapsel plugins are listed there. You don’t need to select them. They will be part of your app by default.

Adding the offline capabilities

Before we discuss the offline capabilities, I want to highlight one thing regarding Developer Companion: since this type of app is typically an online app, we cannot use Developer Companion for developing offline apps. Offline will only work for Packaged Apps.

Users who have already created offline apps before using our cloud build service are probably familiar with the way to add the Kapsel Offline OData plugin to the project. This is actually triggered by adding a “sap.mobile” section that defines ‘stores’ in the project’s manifest.json. We have kept this in the new delivery, so that existing projects can more easily transition. The content of this section is actually not important anymore. What matters is that this section is there. Before the HAT 1805 release, the content was used in the build process. As long as you specify the bare minimum, you are fine:

	},
	"sap.mobile": {
		"definingRequests": {},
		"stores": [
		]

As a result, your Packaged app built with Cloud Build Service includes the SAP Kapsel Offline OData plugin. Now we need to add some more code to make use of this.

Initializing the offline store

The file mobile/hybrid/sap-mobile-hybrid.js contains most of the bootstrap code for the hybrid app. You are free to make changes here. We are going to add a function to create and open the offline store that will be used to store the data relevant for the app on the device.

    openStore: function() {
        console.log("In openStore");
        jQuery.sap.require("sap.ui.thirdparty.datajs");  //Required when using SAPUI5 and the Kapsel Offline Store
        var properties = {
            "name": "store_mainService",
            "host": sap.hybrid.kapsel.appContext.registrationContext.serverHost,
            "port": sap.hybrid.kapsel.appContext.registrationContext.serverPort,
            "https": sap.hybrid.kapsel.appContext.registrationContext.https,
            "serviceRoot": fiori_client_appConfig.appID + "_" + mobile_appRoutes[0].destination,

		"definingRequests": {
			"supplierset": "/Suppliers?$expand=Products"
		}
        };
    
        store = sap.OData.createOfflineStore(properties);
    
        var openStoreSuccessCallback = function() {
            console.log("In openStoreSuccessCallback");
            sap.OData.applyHttpClient();  //Offline OData calls can now be made against datajs.
            sap.Xhook.disable(); // temporary workaround to ensure the offline app can work in WKWebView
            sap.hybrid.startApp();
        }
    
        var openStoreErrorCallback = function(error) {
            console.log("In openStoreErrorCallback");
            alert("An error occurred" + JSON.stringify(error));
        }
    
        store.open(openStoreSuccessCallback, openStoreErrorCallback);
    },  

An important piece to mention here is the serviceRoot specified in the properties. The serviceRoot is basically the location where the offline store will fetch its data, indirectly through Mobile Service. In this particular example I am using the Mobile Service Sample Service; but I could easily change this to use a different destination. The Cloud Platform destination used here was automatically generated by HAT, after triggering the build previously. Also note that the above code assumes one single data source and hence, one single offline store. If your app needs to consume data from multiple sources, you’ll need to open multiple offline stores here.

Updated 19 March 2020: the switch to WKWebView as mentioned in this blog post requires an additional temporary workaround. This is the call to disable Xhook.

In the same file, the appLogon function needs to be adjusted a little bit. Once the logon is done, we call the newly added openStore function, instead of the startApp function.

		if ('serverHost' in context && 'serverPort' in context && 'https' in context) {
			// start SCPms logon
			sap.hybrid.kapsel.doLogonInit(context, appConfig.appID, sap.hybrid.openStore);

Modification of the datasource uri in the manifest file is no longer required. You can use the same project for running it as online as well as standalone offline app. For existing projects that were mobile enabled, it is advised to remove the ‘mobile’ folder from the project (you might want to save a copy) and re-enable for mobile. New projects will automatically get the latest code needed when the project is enabled for mobile.

Avoid cross origin (CORS) issues on iOS

With the introduction of iOS 14, the security aspects were further enhanced. To avoid having your application running into cross origin issues, we need to change the settings in the SAP Mobile Services cockpit for Cross Domain Access. This is especially relevant if your app is using the User API (we had a few users facing this).

Please refer to the screenshot below. Please open the SAP BTP Mobile Services Admin Cockpit. Go to Settings -> Security -> Cross Domain Access. Set Origin field to “<AdminAPI>,null”.

Read-only offline app.

Trigger a new cloud build and download your app on your device. After the authentication screen and passcode screen, the screen will go blank for a while. We should actually show something here, so the end-user knows what is happening. This is an improvement to be added later. At this point, the offline store is being synchronised with the backend database. Once this is done, the UI will show up and display the data (this can take around 15 seconds).

You can put your device into airplane mode and continue accessing the data.

Congrats! You’ve built an offline app.

It is nice to be able to Read data when offline, but how about Create, Update and Delete?

The application template is actually already allowing modifications, removal and adding new data entries. But how do we handle this when the device is not connected to the network? The data is simply stored in the on-device offline store. When network connectivity returns, we can synchronise the changes between backend database and on-device store.

To get the latest changes from the backend, we use the refresh function. To upload locally made changes to the backend, we use the flush function.

For the refresh, the most obvious way of implementing this, is as part of the pull-to-refresh. The flush should be triggered once the network connection is back. But for the sake of this demo and making it easier to test, I’ll just add two buttons to the footer bar: a Refresh and a Flush button.

Let’s open the file webapp/view/Master.view.xml and add the following buttons in the customFooterContent.

	<semantic:customFooterContent>
	    <Button text="Refresh" width="80px" id="__button1" press="onRefreshButton"/>
	    <Button text="Flush" width="70px" id="__button0" press="onFlushButton"/>
	</semantic:customFooterContent>

Now open the file webapp/controller/Master.controller.js and add the following code for handling the button press:

		onRefreshButton: function() {
			if (typeof sap.hybrid !== 'undefined') {
				sap.hybrid.refreshStore();
			}
		},
		
		onFlushButton: function() {
			if (typeof sap.hybrid !== 'undefined') {
				sap.hybrid.flushStore();
			}
		},

I have added the type check to make sure these calls are only done for a hybrid app. The web app should not call these functions.

We will add the functions called in the file mobile/hybrid/sap-mobile-hybrid.js

	refreshStore: function() {
		console.log("Offline events: refreshStore");
		if (!store) {
			console.log("The store must be open before it can be refreshed");
			return;
		}
		store.refresh(sap.hybrid.refreshStoreCallback, sap.hybrid.errorCallback, null, sap.hybrid.progressCallback);
	},

	refreshStoreCallback: function() {
		console.log("Offline events: refreshStoreCallback");
	},

	flushStore: function() {
		console.log("Offline events: flushStore");
		if (!store) {
			console.log("The store must be open before it can be flushed");
			return;
		}
		store.flush(sap.hybrid.flushStoreCallback, sap.hybrid.errorCallback, null, sap.hybrid.progressCallback);
	},

	flushStoreCallback: function() {
		console.log("Offline events: flushStoreCallback");
	},

	errorCallback: function(error) {
		console.log("Offline events: errorCallback");
		alert("An error occurred: " + JSON.stringify(error));
	},

	progressCallback: function(progressStatus) {
		// console.log("Offline events: progressCallback");

		var status = progressStatus.progressState;
		var lead = "unknown";
		if (status === sap.OfflineStore.ProgressState.STORE_DOWNLOADING) {
			lead = "Downloading ";
		} else if (status === sap.OfflineStore.ProgressState.REFRESH) {
			lead = "Refreshing ";
		} else if (status === sap.OfflineStore.ProgressState.FLUSH_REQUEST_QUEUE) {
			lead = "Flushing ";
		} else if (status === sap.OfflineStore.ProgressState.DONE) {
			lead = "Complete ";
		} else {
			alert("Unknown status in progressCallback");
		}
		console.log(lead + "Sent: " + progressStatus.bytesSent + "  Received: " + progressStatus.bytesRecv + "   File Size: " +
			progressStatus.fileSize );
	},

The refresh and flush functions are asynchronous and will take some time. For demo purposes, I’ve kept the implementation rather simple and added some console logging to have an idea of what is going on. For your enterprise mobile applications, you should implement a way to inform the user about progress and update the UI when needed.

After saving all your code changes, please trigger another cloud build. The result will be an app that allows you to work with your data in online, as well as offline mode. You trigger a refresh to update the on-device store. You can flush the changes made in the on-device store to the data service.

What about data conflicts?

There is one important aspect that I will not discuss in this blog: data conflicts. How do you handle errors when data on the service has changed, while your device was offline, and your user also made changes on the same data?

I think this topic is worth a separate blog post.

Using multiple stores or multiple data sources

Over the past months we have received queries from several users asking about the use of multiple data sources. Customers can have heterogeneous landscapes where data is stored in various systems. When you are building a mobile app that is supposed to work in all kinds of network conditions, you have to use an offline store. Your app might need data from multiple systems. Every data source requires its own offline store. So if you are using data from ‘X’ number of data sources in your app, you’ll need to initialise and open ‘X’ number of offline stores.

I have seen customers using one single offline store, while their application needs data from multiple sources. To prevent errors in retrieving data, there were various checks in the code to ensure the device was having a network connection. This approach is not recommended. Instead, you should add additional stores. This could require a different way of exposing the data as well.

You might be asking, “Is there a limit to the size or amount of stores ?”. The answer simply depends on the amount of storage available on the device. You can have ‘N’ number of stores, as long as the size of the total storage space required doesn’t exceed the storage space on device. The question will be: how much storage space do my target devices have available at a minimum, so my app can work well on all those devices ? In other words and as a practical example: I can have 64GB available on my device, but my colleague has only 2GB free. The answer will be ‘less than 2GB’ in that case.

In the example code shown above, I’ve created one ‘openStore’ function, with a specific serviceRoot and definingRequests, and callback functions for success and failure. You can add more ‘openStore’ functions where needed. You will also need to implement flush (if required) and refresh functions for each of the stores created. The key will be the serviceRoot and definingRequests. This is where most developers struggle. If the path doesn’t match, the initialisation will fail. Please check the troubleshooting section below to resolve this.

Take note that once you add the additional data source to the manifest.json file, the UI5 application will try to fetch metadata from this source when the app initialises; no matter whether there is a network or not; and this could trigger an authentication flow, which you might want to avoid under bad network conditions.

Conclusion

With this blog post I’ve shown you how to create an offline app in SAP Web IDE Full-Stack. The example code provided is very basic. There is surely room for improvement to get this to production level apps. My goal was to show you the basics here, to get you started quickly. I hope this blog post is useful to you and I encourage you to try it out yourself.

Our online documentation is here.

Troubleshooting

After providing support to several users, I’m adding this section to help you in resolving issues you might be facing.

  • You can test the URL used for OData access in the Mobile Service admin cockpit. Select the application and go to Connectivity. Make sure you can Ping the destination. Use the OData application destination test (the icon on the left side of Ping). Specify the relative path as used by your app and click next. You should be able to view data available in the service. If this is not working, then your app will not be able to retrieve data either. Resolve this first before continuing to debug your app.
  • When the offline store is initialised on your device, it will be based on the serviceRoot provided. If you want to make changes to the serviceRoot, you have to delete the existing offline store from your device. Otherwise it will still exist when you restart the app, using the serviceRoot it was initialised with. On iOS, you can remove the store by deleting the app. On Android, removing the app is not sufficient. You also need to clear the app data and cache.
  • A common issue for several users is that the initialisation of the offline store fails. In the example code I am using an alert to indicate this. In case of doubts, you can put a breakpoint on the success callback and error callback to see what is happening.
  • In the file mobile/index.html there is some code to adjust your app’s XMLHttpRequests to use URLs that work locally. Please check whether the URL used matches with the serviceRoot you’ve defined. If the path doesn’t match, the app will not use the on-device store.
  • The offline store communicates with Mobile Service over a dedicated channel called Mobilink. You won’t be seeing data exchange in the (web) app’s logs, as this is handled via native code. Using the Trace functionality in Mobile Service admin cockpit (see Analytics), you will be able to generate a HAR file and get detailed information about the communication between the client app and Mobile Service AND ALSO between Mobile Service and your backend. Check for error messages in the traces to understand why there are failures happening.

Reporting issues

Although I have verified and tested the above flow several times and received confirmation from several users that this works, the results for you might be different. This might be due to interruptions in service availability or changes on the various systems involved. Also take note that I have not tested other scenarios (e.g. on-premise, with Cloud Connector) myself.

For users who are trying to add offline capability to their own app and it fails: please try reproducing exactly the same as described above. Once you have this working, it will be a good reference for your  own application. Adding offline capabilities to a hybrid app is not trivial.

In case you are running into issues, please raise a (BCP) ticket for component CA-WDE-MOB. You can also comment down below, but take note that I will not be monitoring this blog very frequently.

Also, please check my latest blog post “What’s new with Hybrid Application Toolkit in 2019” for the latest updates, tips and tricks, ways to resolve issues.

If you found this posting useful, please let me know by hitting the like button.

Thank you!

Assigned Tags

      425 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Jai Sharma
      Jai Sharma
      I'm trying but second openStore is not working can you pls share your code here??
      thanks .
      regard
       jai sharma
      Author's profile photo Krisztian Szanbo
      Krisztian Szanbo

      Hello Jai Sharma,

      I had to change some sensitive data, but here is the code:

       

      /* hybrid capacity bootstrap
       *
       * This has to happen after sapui5 bootstrap, and before first application page is loaded.
       */
      
      sap.hybrid = {
      	loadCordova: false,
      
      	setCordova: function () {
      		sap.hybrid.loadCordova = true;
      	},
      
      	packUrl: function (url, route) {
      		var result;
      		if (route.manual) { // routes requires a manually created Mobile Destination with Rewrite on Backend and via CP App set
      			result = route.path; // keep the original path
      		} else {
      			var connection = (fiori_client_appConfig.appID + "_" + route.destination).substr(0, 63); // max length cap by SCPms DB
      			result = "/" + connection;
      		}
      		var path = url.substring(route.path.endsWith("/") ? route.path.length - 1 : route.path.length); // the remaining URL path
      		result += (route.entryPath ? route.entryPath : "") + path;
      		return result;  
      	},
      
      	appLogon: function (appConfig) { 
      		var context = {};
      		var url = appConfig.fioriURL;
      		if (url && (url.indexOf("https://") === 0 || url.indexOf("http://") === 0)) {
      			if (url.indexOf("https://") === 0) {
      				context.https = true;
      				url = url.substring("https://".length);
      			} else {
      				context.https = false;
      				url = url.substring("http://".length);
      			}
      
      			if (url.indexOf("?") >= 0) {
      				url = url.substring(0, url.indexOf("?"));
      			}
      			if (url.indexOf("/") >= 0) {
      				url = url.split("/")[0];
      			}
      			if (url.indexOf(":") >= 0) {
      				context.serverHost = url.split(":")[0];
      				context.serverPort = url.split(":")[1];
      			}
      		}
      
      		// set auth element
      		if (appConfig.auth) {
      			context.auth = appConfig.auth;
      		}
      
      		// If communicatorId is set then use it to be compatible with existing values. Otherwise, use the default "REST". 
      		// By doing so logon core does not need to send ping request to server root URL, which will cause authentication issue. 
      		// It occurs when the root URL uses a different auth method from the application's endpoint URL, as application can only handle authentication on its own endpoint URL.
      		context.communicatorId = appConfig.communicatorId ? appConfig.communicatorId : "REST";
      
      		// Set disablePasscode to true if you want to hide the passcode screen
      		context.custom = {
      			disablePasscode: false
      		};
      
      		if ("serverHost" in context && "serverPort" in context && "https" in context) {
      			// start SCPms logon
      			sap.hybrid.kapsel.doLogonInit(context, appConfig.appID, sap.hybrid.openFirstOpenStore);
      			sap.hybrid.kapsel.doLogonInit(context, appConfig.appID, sap.hybrid.openSecondOpenStore);
      		} else {
      			console.error("context data for logon are not complete");
      		}
      	},
      
      	bootStrap: function () {
      		if (sap.hybrid.loadCordova) {
      			// bind to Cordova event
      			document.addEventListener("deviceready", function () {
      				// check if app configuration is available
      				if (fiori_client_appConfig && fiori_client_appConfig.appID && fiori_client_appConfig.fioriURL) {
      					sap.hybrid.appLogon(fiori_client_appConfig);
      				} else {
      					console.error("Can't find app configuration probably due to a missing appConfig.js in the app binary.");
      				}
      			}, false);
      		} else {
      			console.error("cordova is not loaded");
      		}
      	},
      
      	loadComponent: function (componentName) {
      		sap.ui.getCore().attachInit(function () {
      			// not support sap.ushell navigation
      			sap.ui.require([
      				"sap/m/Shell",
      				"sap/ui/core/ComponentContainer"
      			], function (Shell, ComponentContainer) {
      				// initialize the UI component
      				new Shell({
      					app: new ComponentContainer({
      						height: "100%",
      						name: componentName
      					})
      				}).placeAt("content");
      			});
      		});
      	},
      
      	openFirstOpenStore: function () {
      		jQuery.sap.require("sap.ui.thirdparty.datajs"); //Required when using SAPUI5 and the Kapsel Offline Store
      		var properties = {
      			"name": "store_first",
      			"host": sap.hybrid.kapsel.appContext.registrationContext.serverHost,
      			"port": sap.hybrid.kapsel.appContext.registrationContext.serverPort,
      			"https": sap.hybrid.kapsel.appContext.registrationContext.https,
      			"serviceRoot": fiori_client_appConfig.appID + "_" + mobile_appRoutes[0].destination +
      				"/sap/opu/odata/sap/it_was_our_first_service_url/",
      
      			"definingRequests": {
      				"ItWasOurFirstEntitySet": "/ItWasOurFirstEntitySet"
      			}
      		};
      
      		store = sap.OData.createOfflineStore(properties);
      
      		var openStoreSuccessCallback = function () {
      			sap.OData.applyHttpClient(); //Offline OData calls can now be made against datajs.
      		//	sap.hybrid.startApp();
      		};
      
      		var openStoreErrorCallback = function (error) {
      			sap.m.MessageToast.show("An error occurred" + JSON.stringify(error));
      		};
      
      		store.open(openStoreSuccessCallback, openStoreErrorCallback);
      	},
      	
      	openSecondOpenStore: function () {
      		jQuery.sap.require("sap.ui.thirdparty.datajs"); //Required when using SAPUI5 and the Kapsel Offline Store
      
      		var properties = {
      			"name": "store_second",
      			"host": sap.hybrid.kapsel.appContext.registrationContext.serverHost,
      			"port": sap.hybrid.kapsel.appContext.registrationContext.serverPort,
      			"https": sap.hybrid.kapsel.appContext.registrationContext.https,
      			"serviceRoot": fiori_client_appConfig.appID + "_" + mobile_appRoutes[0].destination +
      				"/sap/opu/odata/sap/it_was_our_first_service_url/",		// we are using the same route from the appRoutes.js file, but it can change!
      
      			"definingRequests": {
      				"ItWasOurSecondEntitySet": "/ItWasOurSecondEntitySet"
      			}
      		};
      		store2 = sap.OData.createOfflineStore(properties);
      
      		var openStoreSuccessCallback = function () {
      			sap.OData.applyHttpClient(); //Offline OData calls can now be made against datajs.
      			sap.hybrid.startApp();
      		};
      
      		var openStoreErrorCallback = function (error) {
      			sap.m.MessageToast.show("An error occurred" + JSON.stringify(error));
      		};
      
      		store2.open(openStoreSuccessCallback, openStoreErrorCallback);
      	},
      
      	refreshStore1: function () {
      		if (!store) {
      			sap.m.MessageToast.show("The store must be open before it can be refreshed");
      			return;
      		}
      		store.refresh(sap.hybrid.refreshStoreCallback, sap.hybrid.errorCallback, null, sap.hybrid.progressCallback);
      	},
      	
      	refreshStore2: function () {
      		if (!store2) {
      			sap.m.MessageToast.show("The store must be open before it can be refreshed");
      			return;
      		}
      		store2.refresh(sap.hybrid.refreshStoreCallback, sap.hybrid.errorCallback, null, sap.hybrid.progressCallback);
      	},
      
      	refreshStoreCallback: function () {
      		sap.m.MessageToast.show("Offline events: refreshStoreCallback");
      	},
      
      	clearStore1: function () {
      		store.close(sap.hybrid.closeStoreCallback1, sap.hybrid.errorCallback);
      	},
      	
      	clearStore2: function () {
      		store2.close(sap.hybrid.closeStoreCallback2, sap.hybrid.errorCallback);
      	},
      
      	closeStoreCallback1: function () {
      		store.clear(function () {
      			sap.m.MessageToast.show("Clear1 Success");
      		}, sap.hybrid.errorCallback);
      	},
      	
      	closeStoreCallback2: function () {
      		store2.clear(function () {
      			sap.m.MessageToast.show("Clear2 Success");
      		}, sap.hybrid.errorCallback);
      	},
      
      	flushStore1: function () {
      		if (!store) {
      			sap.m.MessageToast.show("The store must be open before it can be flushed");
      			return;
      		}
      		store.flush(sap.hybrid.flushStoreCallback, sap.hybrid.errorCallback, null, sap.hybrid.progressCallback);
      	},
      	
      	flushStore2: function () {
      		if (!store2) {
      			sap.m.MessageToast.show("The store must be open before it can be flushed");
      			return;
      		}
      		store2.flush(sap.hybrid.flushStoreCallback, sap.hybrid.errorCallback, null, sap.hybrid.progressCallback);
      	},
      
      	flushStoreCallback: function () {
      		sap.m.MessageToast.show("Offline events: flushStoreCallback");
      	},
      
      	errorCallback: function (error) {
      		sap.m.MessageToast.show("An error occurred: " + JSON.stringify(error));
      	},
      
      	progressCallback: function (progressStatus) {
      		var status = progressStatus.progressState;
      		var lead = "unknown";
      		if (status === sap.OfflineStore.ProgressState.STORE_DOWNLOADING) {
      			lead = "Downloading ";
      		} else if (status === sap.OfflineStore.ProgressState.REFRESH) {
      			lead = "Refreshing ";
      		} else if (status === sap.OfflineStore.ProgressState.FLUSH_REQUEST_QUEUE) {
      			lead = "Flushing ";
      		} else if (status === sap.OfflineStore.ProgressState.DONE) {
      			lead = "Complete ";
      		} else {
      			sap.m.MessageToast.show("Unknown status in progressCallback");
      		}
      		sap.m.MessageToast.show(lead + "Sent: " + progressStatus.bytesSent + "  Received: " + progressStatus.bytesRecv + "   File Size: " +
      			progressStatus.fileSize);
      	}
      };

       

      Regards,

      Krisztian

      Author's profile photo Jai Sharma
      Jai Sharma

      Hey Krisztian,

      Thanks for the quick response it's working but two same screen show in view .

      first open store display first service view and

      second store display second service view and data is also show in different different view.

      can you explain how to hide second view .

      thanks

      Regards,

      jai sharma

      Author's profile photo Krisztian Szanbo
      Krisztian Szanbo

      Hello Jai Sharma,

      You should start the app once (just in your second open store function):

      sap.hybrid.startApp();

      You should commented out this line in your first open store function.

      Regards,

      Krisztian

      Author's profile photo Jai Sharma
      Jai Sharma

      Hey Krisztian,

      Yes I commented this line but I have two different services If I'm commented  the first service

      sap.hybrid.startApp();

      ..entity set is not working declare under the first service.

      thanks

      Regard,

      jai sharma

      Author's profile photo Krisztian Szanbo
      Krisztian Szanbo

      Hello Jai Sharma,

      Could you copy here your GET method, please?

      Do you have any error code?

      Did you try the synchronous read method?

      Here is an example:

       

      	var sUrl = yournamespace.Component.getMetadata().getManifestEntry("sap.app").dataSources["Z_YOUR_SSERVICE_URL].uri,
      		oModel = new sap.ui.model.odata.ODataModel(sUrl, true);
      	oModel.read("YourEntitySet",		//  Without '/'
      		null, null,
      		false,
      		function (oData) {
      			// this is the success function
      		},
      		function (oData) {
      			//	this is the fail function 
      			var sErrorText = "" + JSON.stringify(oData.responseText.split("\"message\"")[1].split("\"value\"")[1].split("\"innererror\"")[0]);
      				sErrorText = sErrorText.split(":")[1].split("},")[0];
      			var sFinalText = sErrorText.substring(2,sErrorText.length-2);
      			MessageBox.error(sFinalText,
      				{
      					styleClass: "sapUiSizeCompact"
      				}
      			);	
      		}
      	);

      Regards,

      Krisztian

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Hi Krisztian,

      I see you are calling the doLoginInit twice, with the callback go to the different openStore functions.

      Instead of this, I think it is better to daisy chain the openStore functions. This will open the stores in sequence, and only when both succeed, the app is started.

      So at the line where you've commented the startApp, I would call openSecondStore.

      Thanks,
      Ludo

      Author's profile photo Krisztian Szanbo
      Krisztian Szanbo

      Hi Ludo,

      Thanks for the suggestion, you are right.

      I will rewrite it and the code will look better.

      Regards,

      Krisztian

      Author's profile photo SAP Implementation Team Techmahindra
      SAP Implementation Team Techmahindra

      Hello ,

      I have followed all the steps given in the blog. Still I am not able to see any offline data . I get the attached error. Online it works fine.Please help.

      Thanks and Regards,

      Arijit

      Author's profile photo Jai Sharma
      Jai Sharma

      Hey Arijit,

      Please send your sap-mobile-hybrid.js code here .

      I think you not declare service Root and defining Requests proper.

      Thanks

      Regard

      Jai sharma

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Indeed, the tricky part is the service Root and defining request. You can test the data retrieval in the SAP Mobile Services cockpit by opening the app configuration and check the connectivity. You should be able to retrieve data using the applied path.

      Regards,
      Ludo

      Author's profile photo Tanuj Saraf
      Tanuj Saraf

      I am getting the following error when I try to add data to the CRUD application.

      Can someone please help me with this?

      Author's profile photo Tanuj Saraf
      Tanuj Saraf

      Jai Sharma Ludo Noens

      Author's profile photo Jai Sharma
      Jai Sharma

      Hey Tanuj Saraf,

      Please Follow this blog.

      https://blogs.sap.com/2016/02/15/introduction-odata-batch-processing-with-content-id-in-sap-gateway/

       

      Regards

      Jai sharma

      Author's profile photo Hari Krishnan
      Hari Krishnan

      Hello Ludo,

      We have licensed cloud account. After activating HAT i don't see option to select Cloud Build Service? Any ideas?

      Regards,

      Hari

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Hi Hari,

      Please check the prerequisites in our online help/guide. You will need SAP Mobile Services.

      Thanks,

      Ludo

      Author's profile photo Jai Sharma
      Jai Sharma

      Hi Ludo ,

      When we run the app using webide preview, we get multiple records returned by the odata service, but when we run the app as a hybrid app (after performing Build Packaged App) on the device, we get only single record returned by the service. We had set external break point in the backend and noticed that service is returned multiple records in the output but at the app we get only single record (last record of the result set). Can anyone advice why we are getting different response from oData while running the app as an hybrid app.

      Thanks and Regards

      Jai Sharma

      Author's profile photo Somisetty Manojkumar
      Somisetty Manojkumar

      Hi Hari,

      Please provide link for HAT Download.

       

      Regards,

      Manojkumar

      Author's profile photo Tenzin Wangmo
      Tenzin Wangmo

      Package build failed for ios with the developer certificate. Do we need to mandatorily use the distribution certificate?

      Author's profile photo meirav weinstein
      meirav weinstein

      Hi,

       

      "Build Packaged App" failed for Android.

      I get the following error: "fail to get signing profiles:server returned an invalid response".

      when I try to create a signing profile I see the following error:"Mobile services did not return a CSRF token and there is no stored token to use"

       

      Can you please advise?

       

      Thanks,

      Meirav

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Hi Meirav,

      Can you try: log out of SAP Web IDE and log in again.

      Try building again.

      Thanks,
      Ludo

      Author's profile photo meirav weinstein
      meirav weinstein

      Thanks for you reply,

      I tried to log out and than back in.

      Now I see this error:

      Fail to get minimum OS versions: Access unauthorized. Ensure you are assigned to the Administrator role of the correct Mobile Services at the SAP Cloud Platform Cockpit.

      And when I try to create profile, I still encounter the same error: “Mobile services did not return a CSRF token and there is no stored token to use”

      Thanks,

      Meirav

      Author's profile photo Kangana Dash
      Kangana Dash

      Hi Ludo Noens ,

       

      Thanks for the great blog . May I know how you are debuggin this mobile app?  Remote Debugging Chrome Tools( by turning the developer options in Mobile?) or something else? 

      Hi Ludo Noens , Krisztian Szanbo

      May I know what is the connection between refreshStore1 , ClearStore1 , refreshStore2 , clearStore2 and when will be  these functions get triggered?
      As I can see there is no function is calling these functions. Just wanted to  understand how from the UI refresh store request of second view(second service) hits the refreshStore2 method.
      Can you please help me understand this logic.
      Thank you very much
      Author's profile photo Krisztian Szabo
      Krisztian Szabo

      Hello Kangana Dash

      For the debugging:

      -You have to enable the developer options in your mobile.

      -You have to build the app with debugger mode (in the project settings you should switch to debugging mode from release mode).

      -Connent your mobile to the laptop and run the dev remote debugging tool and run the app

      For the function triggering:

      -You can call these functions in your contollers, with the following command:

      Example: sap.hybrid.flushStore2();

      Regards,

      Krisztian Szabo

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Debugging the mobile app can be done using the standard browsers. For Android apps, you typically connect Chrome devtools. For iOS apps, you connect the tools in the Safari browser on OSX.

      The app created in the blog has a refresh button. You could trigger a call to both refreshStore1 and refreshStore2 with this. Or add separate buttons, or implement a pull-to-refresh that triggers the refreshStore relevant for the page you are on. The code in the blog and what I mention here are just examples. It’s up to the developer to implement the user experience here.

      The clearStore functions should be called when the user logs out (not implemented in the example here).

      Regards,
      Ludo

      Author's profile photo Santhosh C
      Santhosh C

      Hi Ludo Noens ,

       

      Thanks for the great blog,

      How can we access the logged in user ID from the hybrid mobile app?

      below code does not work

      if (sap.ushell && sap.ushell.Container) {
          this.sUserID = sap.ushell.Container.getService("UserInfo").getId();
      }

       

      Regards,

      Santhosh

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Hi Santhosh,

      This code will work in the Fiori Launchpad, but not in a packaged mobile app. What you need to do is call the userapi available on SAP Cloud Platform.

      https://help.sap.com/viewer/8e5c63b6470a4255861c259f3b2d9b41/Cloud/en-US/2c5d55588c3f4cee839d4084b5a3a0ef.html?q=userapi

      Regards,
      Ludo

      Author's profile photo Santhosh C
      Santhosh C

      Hi Ludo Noens ,

      I tried implementing the same from above SAP help document.

      • added the route in appRoutes.js
      • created a destination in mobile service cockpit and assigned it to the app

      What I might missing is, how to access the api inside the app, a code sample would be very helpful.

       

      Other thing which I tried was to implement like in the below blog and it works for online but not inside packaged mobile app.

      https://blogs.sap.com/2015/04/16/using-the-hcp-user-api-in-web-ide/

      Regards

      Santhosh

       

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      var userModel = new sap.ui.model.json.JSONModel("/services/userapi/currentUser");

      sap.ui.getCore().setModel(userModel, "userapi");

       

      Author's profile photo Santhosh C
      Santhosh C

      Getting below error log in SCP ms cockpit

      Finished sending GET request to back end https://mobile-xxxxxxxxxx.xxx.hana.ondemand.com/services/userapi/currentUser in 134 ms. HTTP status from the back end is 403.

      Mobile destination configured in SCP ms
      https://mobile-xxxxxxxxxx.xxx.hana.ondemand.com/services/userapi/currentUser

       

      var mobile_appRoutes = [
          {
              "path": "/sap/opu/odata",
              "destination": "Test",
              "originDestination": "Testing",
              "entryPath": "/sap/opu/odata"
          },
          {
              "path": "/services/userapi",
              "manual": true
          }
      ];
      onInit: function() {
          var userModel = new sap.ui.model.json.JSONModel("/services/userapi/currentUser");
          sap.ui.getCore().setModel(userModel, "userapis");
      }

       

      Regards

      Santhosh

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Can you first confirm that accessing the userapi works fine in your web app ?

      I mean, you are wrapping a web app into a Cordova container; but you can actually run/deploy the same app on SAP Cloud Platform. If this works, then the Mobile Destination URL should be set to your web app.

      See attached screenshot.

      Author's profile photo Santhosh C
      Santhosh C

      yes, I'm able to access userapi running on SAP Cloud Platform.

      Now what is that I have to do to change the mobile Destination URL to web app, I'm confused on what exactly I have to do for this change.

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Hi Santhosh,

      For this scenario, you actually need to deploy the application to SAP Cloud Platform as well as build as mobile app. Then in the SAP Cloud Platform mobile services cockpit, configure a destination called userapi for the mobile app, which basically points to the web application's url + the services/userapi (see URL in screenshot).

      Regards,
      Ludo

      Author's profile photo Santhosh C
      Santhosh C

      Thanks a lot Ludo Noens , it worked with the approach you mentioned above.

      Thanks for the quick replies.

       

      Regards

      Santhosh

      Author's profile photo Adam Harkus
      Adam Harkus

      Hi Ludo.

      We're having intermittent issues with offline oData FLUSHING.

      An Error occurred while FLUSHING... sometimes appears as an alert on the Device. This is intermittent for around half our users, all with the same devices.

      I checked the logs in SAP Mobile Services which shows the following.

      Request HTTP POST https://mobile-uwjsrxe35t.hana.ondemand.com:443/com.sap.webide.xfdc1737b2ebd4fd3887a7fc11dc5ecef_RSA/ResultOfflineSet failed against the backend with response code: 400 and message: An exception was raised...

      So it looks like an issue with FLUSHING the offline oData to SAP. I know the oData updates are held in a queue on the device and are retried on every FLUSH, but the situation we have at the moment is the queue isn't clearing..... UNTIL WE CLEAR THE APP CACHE ON THE DEVICE.

      Once this is done, the oData is FLUSHED/REFRESHED correctly and no oData is lost.

      Rather than having to clear the app cache every time the issue arises, we're looking to establish the root cause of the issue, to prevent this from happening.

      Can you help?

       

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Hi Adam,

      We've seen the support request you've raised and we will follow up on this through the ticket.
      One thing I'd like to check regarding the devices: are you seeing this on iOS and Android, or only Android (since you mention clearing the app cache ...).

      Thanks,
      Ludo

      Author's profile photo Adam Harkus
      Adam Harkus

      Thanks Ludo.

      Our company mobiles are Andriod only. Samsung A20s i think..

      Strange one, almost all of the Mobile service errors are the same, the initial POST (create) method in the ResultOffline Entity in SAP.

      The logic couldnt be simpler: Take the structure passed in from the Gateway and perform an INSERT on the DB table. I

      Ive no idea why it would intermittently fail, or more confusingly why clearing the app cache seems to resolve it.

      Let's see what's in the error logs.

       

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Did you enable repeatable requests on mobile services ?

      Thanks,
      Ludo

      Author's profile photo Adam Harkus
      Adam Harkus

      I'm not sure. How do I check?

      Author's profile photo Adam Harkus
      Adam Harkus

      Hi Ludo.

      I’ve nothing related to repeatable requests in the application code.

      I’ve also checked the Mobile Services. Here is our offline configuration that also doesn’t mention repeatable requests.

      We use SAP Gateway so isn’t repeatable request ON by default?

       

      Destination Name
      com.sap.webide.xfdc1737b2ebd4fd3887a7fc11dc5ecef_RSA

       

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Hi Adam,

      It was just hunch ... it looks like your issue is not related to it.

      Please capture the network trace and upload client database if possible. My colleagues will follow up on this via the ticket (which is now for your action).

      Thanks,
      Ludo

      Author's profile photo Adam Harkus
      Adam Harkus

      Hi Ludo.

       

      Do you have any detail on the error logs to check in the Gateway and ECC?

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Hi Adam. No, sorry. This is something where our support will have to help. Cheers, Ludo

      Author's profile photo Adam Harkus
      Adam Harkus

      Some extra Info...

      I've upped the logging level in Mobile Services to show how we arrive at the failed backend response..

      Does this help?

      2020-11-25 11:14:22.163 WarningWarning issued b7ecef7f-ae6a-4013-b999-ce763a21ac4b Offline Request HTTP POST "https://mobile-uwjsrxe35t.hana.ondemand.com:443/com.sap.webide.xfdc1737b2ebd4fd3887a7fc11dc5ecef_RSA/ResultOfflineSet" failed against the backend with response code: "400" and message: "An exception was raised.".
      2020-11-25 11:14:22.163 InformationEntry successfully validated b7ecef7f-ae6a-4013-b999-ce763a21ac4b Offline Finished request HTTP POST "https://mobile-uwjsrxe35t.hana.ondemand.com:443/com.sap.webide.xfdc1737b2ebd4fd3887a7fc11dc5ecef_RSA/$batch". Response code was "202".
      2020-11-25 11:14:21.790 InformationEntry successfully validated b7ecef7f-ae6a-4013-b999-ce763a21ac4b Offline Sending batch request HTTP POST "https://mobile-uwjsrxe35t.hana.ondemand.com:443/com.sap.webide.xfdc1737b2ebd4fd3887a7fc11dc5ecef_RSA/$batch".
      2020-11-25 11:14:21.790 InformationEntry successfully validated b7ecef7f-ae6a-4013-b999-ce763a21ac4b Offline HTTP POST "https://mobile-uwjsrxe35t.hana.ondemand.com:443/com.sap.webide.xfdc1737b2ebd4fd3887a7fc11dc5ecef_RSA/ResultOfflineSet" associated with custom tag "" is being added to the batch request.
      2020-11-25 11:14:21.786 InformationEntry successfully validated b7ecef7f-ae6a-4013-b999-ce763a21ac4b Offline Preparing a batch request.
      Author's profile photo Adam Harkus
      Adam Harkus

      Hi Ludo.

      We've also noticed some requests aren't even leaving the device and getting to SAP Cloud Platform UNTIL the App Cache is cleared.

      Any ideas? As a stop-gap is it possible to implement an app-cache clear within the app itself?

      Author's profile photo Adam Harkus
      Adam Harkus

      Hi Ludo.

       

      To confirm we have identified our back-end issue. This was a request coming through with a duplicate key, the ABAP function was trying to INSERT this and failing.

      We have a workaround (ingore the 'exists' error), but we still don't know why the initial request wasn't cleared from the device request queue if it had already been processed.

      Author's profile photo Subha Palanichamy
      Subha Palanichamy

      hi guys,

      Please help me for the below issue.

      We are trying to convert a Fiori app to a mobile app using the Hybrid App Toolkit.

      But when we try to build the app using Build Packaged App, we are getting the error "Fail to get minimum OS Versions:403 Access unauthorized. Ensure you are assigned to the Administrator role of the correct Mobile service at the SAP Cloud Platform Cockpit".

      Although mobile service admin role have been added, we cannot proceed from this point due to this error.

      Any guidance on how to overcome this would be of great help.

      have created forum as well in sap community link : https://answers.sap.com/questions/13194967/sap-hybrid-app-toolkit-fail-to-get-minimum-os-vers.html

       

       

      Thanks in advance.

       

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Please check the section "Prerequisites for Developing Apps Using Mobile Services" here.

      Also, I recommend raising a support ticket for component CA-WDE-MOB.

      Thanks,
      Ludo

      Author's profile photo Subha Palanichamy
      Subha Palanichamy

      thanks for your replay Ludo. let me check ..

       

       

      Regards,

      Subha

      Author's profile photo Subha Palanichamy
      Subha Palanichamy

      Hi Ludo,

      now am facing error like "Failed to get supported sapui5 version :500" at the same time packaged SAPui5 version is empty  and am getting minimum OS version,when am trying to do the build packaged app my project ui5 version is 1.52 my fiori system ui5 version is 1.60.

       

      please guide me am new to this hybrid  mobile app

       

      Author's profile photo Subha Palanichamy
      Subha Palanichamy

      hi Ludo,

      i just tried in different browser so i can crack the above error .before i have used in crome now i have used edge.but bad luck in the apk file also downloaded when i try to install "app not installed " one pop up came .

       

      please guide me

      Author's profile photo Samir Nigam
      Samir Nigam

      Hi Subha,

      For the first issue – “Failed to get supported ui5version: 500” – there may have been some intermittent issue that caused the failure. You can try again in Chrome and if that fails while Edge is working fine, try to clear Chrome cache and see if that helps. If not, open an incident for us to investigate.

      For the “app not installed” issue, looking at the logs on our end, there appears to be some client (your browser/network) issue which was seen as a socket timeout on the server-end (ours). That means the download didn’t complete and the likely reason the APK is corrupted and failing to install. You can try to download again (suggest Chrome to download) from the “Build Results” dialog/page. You do not need to rebuild.

      Please check the size of the APK with the file size mentioned in the build log (at the tail end of the file) to confirm it matches.The build log should have a line such as this one:

      UploadApp for android started. File size is 116977609 bytes

      If APK installation still fails after matching APK file size, I recommend opening another incident for us to investigate and include the full build log and your WebIDE URL. We would need the APK as well if you have a way to share via a secure share site, or else we will provide a URL once you open a ticket.

       

      Author's profile photo Mamatha Majji
      Mamatha Majji

      Hi Ludo,

      I have gone through all your blogs on Hybrid Apps. All are explained in a clear way. Your content is very helpful for who are new to Mobile Apps especially working on Hybrid Apps offline.

      while doing batch calls for POST method  in offline mode for Hybrid App it is going to error function. it is working fine in online Mode. Unable to get the error is there any syntax to store the created Data in offline Mode.

      Please suggest how to overcome it.

      Regards,

      Mamatha M

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Hi Mamatha,

      Using the offline store should be completely transparent. There is no need to write specific code / queries for it.

      My recommendations:

      • Check for errors using Mobile Service cockpit's Trace. All communication regarding offline OData is going over a dedicated channel (Mobilink). You won't see any of this in your browser's debugging tools. If you have access to the backend system connected to it (e.g. Gateway), then try to get the logs there as well. You should be able to find the reason for the error.
      • Contact our support. They know who to contact for help and will be available even during the holidays period. This should be a support ticket for the Mobile SDK, not Hybrid Application Toolkit.

      When contacting support, provide more information about the system you are interfacing with, and which OData version (V2 or V4) you are using.

      Regards,
      Ludo

      Author's profile photo Mamatha Majji
      Mamatha Majji

      Thanks Ludo for your Quick response. I Will follow as you suggested.

      Regards,

      Mamatha

      Author's profile photo Manoj Kumar
      Manoj Kumar

      Hi Ludo,

      Thanks for sharing great blog, I have tried to use USER API and i have an error with response below, Could you please let me know if i have missed any configuration.

      Error:

      Request URL:
      https://mobile-zzffrrr.ca1.hana.ondemand.com/services/userapi/attributes (404 not found)

       

      Code Snippet:

      var userModel = new sap.ui.model.json.JSONModel(); userModel.loadData("/services/userapi/attributes", null, false); this.getView().setModel(userModel, "userapi");

      Thanks in Advance

      Manoj Kumar

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Hi Manoj,

      The url should be the deployed web application. See also previous comments on this blog post.

      Regards,
      Ludo

      Author's profile photo Manoj Kumar
      Manoj Kumar

      Thanks Ludo for your Quick response. I Will follow as you previous comments.

      Author's profile photo Mamatha Majji
      Mamatha Majji

      Hi Ludo,

      Can we login to the Hybrid Mobile App in offline mode and access the data same as in online mode we are using SAML Authentication Mode to access the application in online Mode.

      when we open the App in offline mode getting the blank screen not getting the login page also to login the app.

      Should we have to login to the app once in online mode to get the data in offline mode.

      could you please suggest your ideas

      Regards,

      Mamatha

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      In offline mode, the SAML authentication is skipped. If you've configured the use of a passcode, then the user will still have to enter this passcode (or other way is to provide an id, such as Face ID) to open up the app.

      You surely need to login once and synchronise the offline data store on your device before you can use the app in offline mode.

      If you are getting a blank screen, then there might be some error during the app's initialisation. Please hook up a debugger to find out the reason for the blank screen.

      Thanks,
      Ludo

      Author's profile photo Mamatha Majji
      Mamatha Majji

      Hi Ludo,

      Thanks for your suggestion. We will look into it.

      Regards,

      Mamatha

      Author's profile photo Angel Gutierrez Bellido
      Angel Gutierrez Bellido

      Hi Mamatha,

      have you resolved the problem. If you resolved, you could share the solution.

      Regards,

      Angel

      Author's profile photo Mamatha Majji
      Mamatha Majji

      Hi Angel,

      I hooked up a debugger to mobile App then found the error and cleared.

      Regards,

      Mamatha

      Author's profile photo Antony John
      Antony John

      Hello Community..

      I am getting an HTTP 403 code because of CSRF Token Validation when I am calling the flushStore() and refreshStore() from my UI5 application.

      I am using the standard UI5 ODATA library to POST entities to mobile services from my mobile app

      Is there some configuration I need to do at the destination level for Mobile Services to accommodate for this scenario.

      HAR Screen shot

      HAR%20Screen%20shot

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Hi Anthony,

      Flushing or refreshing the offline store should not trigger issues with CSRF token. It seems this is happening in online mode ?

      It would be good to share the actual request as well, instead of just the response.

      Also, I really recommend raising a support ticket so our team(s) can follow up.

      Thanks,
      Ludo

      Author's profile photo Antony John
      Antony John

      Hello Ludo..

      thanks for your response.

      Could you please send me the link and the component ID I should use while creating the ticket so that it gets routed to the correct team?

      Thanks again for your help

      Tony

       

      Author's profile photo Antony John
      Antony John

      I think I forgot to mention, The screen shot attached above is from the .HAR file captured from Mobile Services. When the Mobile Services try to POST the data received from the device to the SAP gateway server.

      Are there any settings on the Mobile Services destination side of things that will help us, to manage x-csrf tokens.?

      Author's profile photo Pallavi Uppuleti
      Pallavi Uppuleti

      Hi All,

      I have been trying to use the API Management service with the offline app and its not working so far. I have added the destination needed and also set the code in sap-mobile-hybrid.js file. I am not getting any metadata calls while debugging the binary either. Not sure how to proceed further. Kindly advice on what to do next. Below are some screen shots of the configurations and console error:

      Metadata%20failed%20error:

      Metadata failed error

      API%20Endpoint%20destination

      API Endpoint destination

      appRoutes.js%20file

      appRoutes.js file

       

      Any kind of help is appreciated.

       

      Thanks and Regards,

      Pallavi

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Before trying to access the endpoint in the mobile app, please try to access the endpoint in the Mobile Services cockpit via the mobile destination. It should give you the service metadata. If this fails, then you might need to adjust your path.

      Take note that your mobile app will use the mobile destination defined in Mobile Services; not the destination defined in SAP Cloud Platform.

      Thanks,
      Ludo

      Author's profile photo Pallavi Uppuleti
      Pallavi Uppuleti

      Hi Ludo,

       

      Thank you for the quick reply. i have configured the destinations in Mobile Services cockpit too. I tested it via the Connectivity feature of the assigned application. I am getting the data needed here but not in the offline application. Please find below the screenshot of data received (some parts are redacted)

       

      Thanks and Regards,

      Pallavi

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      In this case, I suspect your serviceRoot is not correct.

      Author's profile photo Srikar Nagadevara
      Srikar Nagadevara

      Hi Ludo,

      We have worked on SAP Mobile services and as a result we found we have 2 services activated in the SAP BTP.

      1. Mobile Services, Consumers (Build and run mobile apps on B2C use cases)
      2. Mobile Sevices, Users (Build and run mobile apps on B2E & B2B use cases)

      a) We started working choosing the option 2 under Development & Quality but we do not know what we can choose for production.

      b) Does they have any restrictions we choose the wrong one in production?

      c) We see both the options are enabled under the services pane. Does it get billed if both options 1&2 were enabled but option 1 is not in usage?

      Could you please let us know your suggestions?

      Thanks&Regards

      Srikar N

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Hi Srikar,

      The functionality is the same and you can switch plans. The difference is in the pricing. The consumers version is in blocks of users, with a fixed charge per month. The second option is more like a pay as you go option, where more users means more charge.

      Unless you are ramping up for mass deployment, you should use option 2. If you have not used option 1, then I recommend to disable it to avoid charges.

      I recommend talking to your SAP account executive about the pricing, to understand which plan suits your situation.

      Thanks,
      Ludo

      Author's profile photo Srikar Nagadevara
      Srikar Nagadevara

      Hi Ludo,

      Thanks for your suggestion. We tried to disable consumers but unfortunately, my users also disabled and all deployed apps under users got removed and the page looks empty.

      Again when I tried to deploy the new apps from Web IDE or Create a new app manually from the SMP, it gave me this error

      Could you please suggest to me?

      Thanks

      Srikar N

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      You have a cloud platform destination called mobileservices that is pointing to the instance you've disabled. The error indicates that the Hybrid Application Toolkit extension is not able to reach SAP Mobile Services.

      You can fix this by updating the destination.

      Regards,
      Ludo

       

      Author's profile photo Srikar Nagadevara
      Srikar Nagadevara

      Thanks Ludo, but looks like this is failing randomly.

      We have seen the mobile services destination under SAP Cloud Cockpit and it is the pointing to our SMP. Since we are able to create apps randomly from SAP Web IDE I am confused why is this failing for a while and for 10 min it starts creating apps to SMP.

      We do not understand why this is behaving weird ? This has been taking place after the consumers tile is disabled. Does this have any connection or bug from SAP side?

      Please suggest your thoughts.

      Srikar N

       

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Hi Srikar,

      Please raise a support ticket for this, so this can be routed to the correct team. I don't have an answer to why this is behaving like this.

      Thanks,
      Ludo

      Author's profile photo Sergei Belyakov
      Sergei Belyakov

      Good day!

      Tell me where to look more specifically at the account address and how to correctly register the URL when creating the Destination Configuration?

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Hi Sergei,

      Are you using a Neo and Cloud Foundry landscape ?

      My blog post mentions Neo Trial, which is no longer available. If you are using a Neo (non-trial) account, then the instructions still apply.

      Thanks,
      Ludo

      Author's profile photo Sergei Belyakov
      Sergei Belyakov

      I'm using a paid Neo cloud, but I can't find the right URL (find the embed number). Can you help with the correct link?

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      As mentioned in my response on the other blog post, I don't think you have a SAP Mobile Services subscription.

      Author's profile photo Manoj Kumar
      Manoj Kumar

      Hi Ludo,

      Login User Api is working fine on Android Devices but IOS User Api is failing.Could you please help me.How to resolve the issue on IOS Device.

       

      Thanks and Regards,

      Manojkumar

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Hi Manoj,

      You probably need to activate CORS in SAP Mobile Services to resolve this.

      Please refer to the screenshot below. To workaround the cross origin issue, please open the SAP BTP Mobile Services Admin Cockpit. Go to Settings -> Security -> Cross Domain Access. Set Origin field to “<AdminAPI>,null”.

      Hope this helps.

      Regards,
      Ludo

      Author's profile photo Manoj Kumar
      Manoj Kumar

      Thanks Ludo for help .Now it working fine on IOS Device.

       

      Regards,

      Manojkumar

      Author's profile photo Jai Sharma
      Jai Sharma

      Hello Ludo,

       

      I have question about how to deploy offline application into development server because when we bulid over application we scan the QR code for download the application after deploy in development how we get QR code . If you have any suggestions please let me know .

       

      Thank you

       

      Regard

      Jai

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Hi Jai,

      I am not completely clear what your question is, but let try to answer...

      When you build the application through our cloud build service, the app (configuration) will automatically be created in SAP Mobile Services. Once the build is done, you will see a QR code that you can use to download & install the application on your device. The QR code basically represents a URL to our document service, where your app's IPA and/or APK file is stored.

      So, in a development environment, you don't need to deploy the app to a server. SAP Mobile Services is taking care of this.

      For distributing the app binaries, it is recommended to use a (non-SAP) MDM solution.

      Hope this answers your question.

      Regards,
      Ludo

      Author's profile photo JaiR S
      JaiR S

      Hello ludo,

      Thank you for replying me your answer is right but when same thing we use in production server then how we get QR code form production server because in production we don't have acces to  bulid a project and generate the QR code.

       

      Regard

      Jai

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Hi Jai,

      You would need to migrate the project to another space. For detailed steps, see the frequently asking questions blog here:

      https://blogs.sap.com/2020/04/03/best-practices-faq-and-tips-for-developing-hybrid-apps-with-hybrid-app-toolkit/

      This obviously requires you to have access to this Prod landscape, or you have to instruct the owner (admin) to perform this.

      Thanks,
      Ludo

      Author's profile photo Nagaraj J
      Nagaraj J

      Hi Ludo.

      I am facing  data downloading issue.Could you please help me..

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      At first glance, it looks like the property in your data entity is not compatible. Is it possible for you to change this to a compatible type ?

      Please raise a support ticket for component CA-WDE-MOB and our support engineers will direct it to the correct team for follow-up.

      Thanks,
      Ludo

      Author's profile photo Nagaraj J
      Nagaraj J

      Thanks Ludo

      Author's profile photo Rajesh Jayasankar
      Rajesh Jayasankar

      Hi Ludo,

      Good Day!

      I am trying to create a simple offline hybrid app using Cordova and kapsel sdk, but I am unable to register my app with mobile service from cloud foundry.

      After passing user id and credentials in logon screen sap.Logon.init doesn't give any context neither success or error. the app display blank screen.

      can you please help to successfully register my offline app with the mobile service application.

      please find the service.json file which I am using for registering offline app

      "logon": true,
      "host": "mobile-service-cockpit-web.cfapps.eu10.hana.ondemand.com/cockpit/v1/org/xxxxxxxx/space/xxxxxxxx",
      "port": "443",
      "https": "true",
      "appID": "com.sap.hybridapp.demo",
      "network": true,
      "auth": [
      {
      "type": "oauth2",
      "config": {
      "oauth2.authorizationEndpoint": "https://xxxxxxxtrial-dev-com-sap-hybridapp-demo.cfapps.eu10.hana.ondemand.com/oauth2/api/v1/authorize",
      "oauth2.tokenEndpoint": "https://xxxxxxxtrial-dev-com-sap-hybridapp-demo.cfapps.eu10.hana.ondemand.com/oauth2/api/v1/token",
      "oauth2.endUserUI": "https://xxxxxxtrial-dev-com-sap-hybridapp-demo.cfapps.eu10.hana.ondemand.com",
      "oauth2.clientID": "d11a1e43-cc0c-43d9-b04b-604ac099a5d1",
      "oauth2.grantType": "authorization_code"
      }
      }
      ],
      "communicatorId": "REST"

      Thank you in advance

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Not sure whether this blog helps: https://blogs.sap.com/2019/05/10/how-to-adapt-hybrid-apps-created-with-hat-to-use-mobile-services-on-cloud-foundry/

      Author's profile photo Fitha Zakkeer
      Fitha Zakkeer

      Hi Ludo

      The offline odata's capacity depends upon our device's capacity, right?

      Also what is the retention period of the offline odata?

      Can we add any retention warning to our customer?

      How to warn user when the user is close to limit of storing offline data?

      Please help in solving these doubts 🙂

       

      Author's profile photo Saurabh Singh
      Saurabh Singh

      Hi Ludo Noens

      I am unable to load application. I am getting white screen as shown below:

      serviceRoot defined as shown below:

      var properties = {
      			"name": "store_mainService",
      			"host": sap.hybrid.kapsel.appContext.registrationContext.serverHost,
      			"port": sap.hybrid.kapsel.appContext.registrationContext.serverPort,
      			"https": sap.hybrid.kapsel.appContext.registrationContext.https,
      			"serviceRoot": fiori_client_appConfig.appID + "_" + mobile_appRoutes[2].destination + "/getProductDetail?itemEntitySetName=CustomSet",
      			"definingRequests": {
      				"Customers": "/CustomSet"
      			}
      		};

      appRoutes.js

      var mobile_appRoutes = [
      	{
      		"path": "/services/userapi",
      		"manual": true
      	},
      	{
      		"path": "/somePath",
      		"destination": "somePath"
      	},
      	{
      		"path": "/Java_proxy",
      		"destination": "Java_proxy",
      		"originDestination": "Java_proxy"
      	}
      ];
      

      Odata request via java proxy web service in application:

      $.ajax({
          url: "/Java_proxy/getProductDetail?itemEntitySetName=CustomSet",
          type: "GET",
          contentType: "application/json",

      Actual odata service path and request:

      https://hostname:port/sap/opu/odata/odataService/Custom(userID='P0000')/Set

      Connectivity:

      On 'Ping' button click, 'Ping successful' message is shown.

      On click of 'OData Application Destination Test', when add relative path of java proxy web service and click Next , it shows 'Cannot get entity' message.

       

      When the whole java proxy url is run on browser, it fetches proper data from SAP via odata and displays them as well.

      Java proxy url =https://javaproxysubaccount.hana.ondemand.com/service/product/getProductDetail?itemEntitySetName=CustomSet

      Please suggest where am i going wrong.

       

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      See https://blogs.sap.com/2020/04/03/best-practices-faq-and-tips-for-developing-hybrid-apps-with-hybrid-app-toolkit/

      Hook up a debugger, and in parallel, make use of the logging feature in SAP Mobile Services to understand why the initialisation fails.

      Regards,
      Ludo

      Author's profile photo Daniel Garcia Albandea
      Daniel Garcia Albandea

      Hello Ludo,

       

      It is possible create and deploy to Mobile Services an hybrid application from Business Application Studio instead of Web IDE?

      I know that best practices recommend use MDK, but we are looking for alternatives in order to migrate a SAPUI5 developed app with offline capabilities.

      Thanks in advance,

      Author's profile photo Ludo Noens
      Ludo Noens
      Blog Post Author

      Hi Daniel,

      Hybrid app development was not migrated to SAP Business Application Studio and there are no plan to do so in future.

      The only option I can give is moving the project out of SAP Web IDE and manage it locally.

      https://blogs.sap.com/2020/06/27/how-to-migrate-your-hybrid-mobile-hat-project-out-of-sap-web-ide-full-stack/

      This will obviously require more hands-on work to deploy to Mobile Services (I assume on Cloud Foundry).

      Regards,
      Ludo

      Author's profile photo Daniel Garcia Albandea
      Daniel Garcia Albandea

      Hi Ludo,

      Thanks for your quick response 🙂

      Best Regards,

      Author's profile photo Manoj Kumar
      Manoj Kumar

      Hi Ludo,

      I am facing an issue Latest IOS Version 15. Till IOS Version 14 is working fine. The upgrading version is not working.could you please help me how to resolve it.

      Regards,

      S Manojkumar

      Author's profile photo Zhigao Chen
      Zhigao Chen

      Hi Manoj,

      The mac machines of our cloud build service were upgraded to MacOS Big Sur, which allows building apps for iOS 15, in early Sep. Please rebuild your app to see if you still get this error.

      Regards,

      Zhigao

      Author's profile photo Manoj Kumar
      Manoj Kumar

      Hi Zhigao,

      Working fine.Thank you !!!

      Regards,

      S Manojkumar