Skip to Content

Updated 12 July 2018: 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 and re-enable for mobile. New projects will automatically get the latest code needed when the project is enabled for mobile.

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. Last December, 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. Our colleagues from SAP Web IDE are now advocating 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

Updated on 30 May 2018: changed data source to Mobile Service sample data service.

Before we start creating the app, let’s 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/SampleServices/ESPM.svc. I am using a trial account for this blog post. You’ll obviously have to change the url for productive environments.

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

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.

When using a trial account, you will use the Cloud Build feature based on Mobile Service for development and operations. Customers who 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 Service for development & operations by default. If you do see the radio buttons, please ensure the radio button regarding Cloud Build Service is enabled for Mobile Service for development & operations.

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.

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.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.

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.

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.

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. You can easily get started using our trial landscape.

Our online documentation is here.

 

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) 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, you can leave a comment below. For SAP customers, partners and colleagues: please raise a (BCP) ticket for component CA-WDE-MOB.

Thank you!

To report this post you need to login first.

210 Comments

You must be Logged on to comment or reply to a post.

  1. SHADAAN QUAISER

    Hi Ludo,

    I am trying to add my gateway box to SCPms via cloud connector. I am able to register the backend via cloud connector. I have a couple of question here.

    1. While registering backend via HCC on SCPms what would be the the SSO mechanism.
    2. While creating mobile destination in SCPms with proxy type Cloud connector the url would be virtual IP and port that we have created in HCC while adding gateway. right?
    3. On testing destination always its give me below message.

    4. Then i am using this as a destination to SCP with appending the service url i.e; “/sap/opu/odata/sap/service_name”

    5. Here in the user i tried adding backend user and Pxxxxxxx user also every time i am getting 403 forbidden error.

     

    Am i missing anything or any roles.Please suggest me the solution, i am stuck here for a week.

     

     

    Regards

    Shadan

    (0) 
  2. R AVINASH KUMAR

    hi ludo,

     

    i followed you steps , but  while assessing the mobile service , i am getting forbidden error 403. Actually i was able to replicate your application , Now i am trying with my own service .Are there any user roles or permission that has to be provide to assess the mobile service in destinations.

    (0) 
    1. Ludo Noens
      Post author

      If you were able to replicate my app, then you have all the user roles and permissions you need.

      It is more likely that you have not configured the destination properly. You can test the connection from the Mobile Services admin cockpit. If the ‘ping’ works and you are able to get some (meta)data, then this part is fine.

      If this doesn’t help, you have two features in Mobile Services that can help identify the problem. In the Analytics tab, check the logs. If this doesn’t tell you enough, you can use the trace function to record the network activity related to your app. Take note that when debugging your app through Chrome (Android) or Safari (iOS), you will not be able to see the exchange done over mobilink, which is the protocol used by the offline store. But you can see it in the traces.

      Hope this helps.

      (0) 
  3. R AVINASH KUMAR

    Hi ludo

    the steps that i follwed where

    1. first i created a service in Destination with the backend system. I am passing just the bankend system general url .(everything is fine here).
    2. the i created a mobile destination using this cloud destination (ping is succesfull here).
    3. while accessing the api for destination of that particular application i am getting
      HTTP Status 403 – Neither Application connection id nor Application id is provided.

      4. then i am trying to use this mobile service in my cloud destination there i am getting 403 forbidden error.

    Questions:

    1. should i pass full service url in the cloud destination including my oadta service name?

    2.while creating the mobile destination is  there a requirement of header that should be passed ?.

     

     

    (0) 
    1. Ludo Noens
      Post author

      I suspect there is a problem with the destination you’ve configured. Although you can ping it successfully, you are getting a 403 at step 3. Try to resolve this first, before trying to consume it through mobile services.

       

      (0) 
      1. R AVINASH KUMAR

        Can youhelp with the steps if I create using my own odata service.

        If I create mobile destination and use it in for webide destination , I don’t get the link between the application I’d  and destination before application is even created . I am new to this . Am I missing some step?

         

         

        (0) 
    1. Ludo Noens
      Post author

      Hi Angel,

      I recommend using the CRUD or Master-Detail template as your starting point. The SAPUI5 template is rather barebones and you will need to add a bunch of libraries yourself.

      Cheers,
      Ludo

       

      (0) 
  4. Tyler Fincham

    Hi,

    How did you manage to put a breakpoint in the sap-mobile-hybrid.js ? If I put a breakpoint there nothing happens because my code doesn’t reach it.

    (0) 
    1. Ludo Noens
      Post author

      Hi Tyler,

      When you initially start the app, you will probably not be able to trigger a breakpoint as the code execution has already happened before you are able to do so. Reloading the web app in Safari (or Chrome in case of Android) will allow you to break in this code.

      Regards,
      Ludo

      (0) 
  5. Luis Cortez

    Hi Ludo,

     

    Great blog ! I was able to replicate your example. In a separate app I was also able to use my own OData (using my on premise DEV system). I have a question:

    When I am offline, let’s say I want to validate one input field , for example customer. How can I do a look up to a master table for validation? Should the master table be part of the OData?  Do you have any suggestions on how to do a look up?

    Louis

     

    (0) 
    1. Ludo Noens
      Post author

      Hi Luis,

      How would you do this for an online app? If you are using an OData source for the master table look up, then it makes sense to offline that table as well. The way to implement it is just like when your source is online.

      You can create multiple offline stores if needed.

      Cheers,
      Ludo

       

      (0) 
    2. R AVINASH KUMAR

      Hi louis

      Can you help me with the steps if I am consuming my odata services.

      Actually the steps before consuming mobile destination in web ide.that ludo has described.

      (0) 
        1. R AVINASH KUMAR

          Hi Luis ,

          • I created cloud destination for webIde basic application .
          • I created a simple list application where i binded the data from the service.
          • And i added a button on the screen on click of which i can add random data to the list  , through create service.
          • This application is fully functional in chrome.  

          Now for the mobile application part .

          1. I enabled the hybrid toolkit feature and , i right clicked on the application and enabled for the application .
          2. Then i build the companion application.
          3. Mobile folder got generated in my application.
          4. I went to mobile services and check if my application is generated . There my application was generated and connection was generated .
          5. Now i tried to open the application in my mobile .
          6. Application gets opened but i don’t receive the data .
          7. when i try to create the data it gets created .In the error log it shows application is in offline mode.

          If I create a app and add service to that , and try to use that in Scp destination. it gives 443 forbidden error .

          If i use the sample mobile service as mentioned by ludo ,read service on offline works fine .

          Can you please guide me through end to end steps . I am working on hybrid application for first time.

           

          Thanks i advance .

          (0) 
    1. Ludo Noens
      Post author

      This can be avoided by setting the following value in config.xml:

      <preference name=”SAPKapselHandleX509Challenges” value=”false” />

      However, currently there is no way for you to change this value from SAP Web IDE. We are planning an enhancement for this.

      Meanwhile, if really needed, you can change this by downloading the Android project, modifying the file and rebuilding the project locally.

       

      (0) 
        1. Ludo Noens
          Post author

          config.xml is added by Cordova during a Cloud Build. It is currently not available in the SAP Web IDE project.

          As indicated earlier, you won’t be able to modify this file now. We are working on an enhancement for this.

          (0) 
  6. Kunj Bihari Shukla

    Hi Ludo,

    Nice blog, very informative and helpful. Though While following the blog and trying to explore more on offline functionality I faced some issues, will really appreciate your comments on the below points.

    I have followed all the steps as mentioned while using a simple custom service (entity1: parent, entity2: child association 1:n from parent to child) custom odata service. Overall it works fine but with some issues (all issues faced are with the mobile device):

    1. Create an entry -> flush -> refresh -> refresh the list -> leads to duplicate entries in the mobile one local and the other from the server.
    2. Delete operation fails with the message “cannot delete this entity instance. Another entity instance depends on it”, and I did put a breakpoint in the backend dpc_ext class method but its not hitting it and also it works fine in desktop. (I want to debug this scenario but not sure how we can debug, please suggest if possible)
    3. Also please suggest if there is any way to debug the file sap-mobile-hybrid.js file(or any other way to debug and understand the offline functionality better like can we play around with the offline store at the client) . I did import the project in android studio but was unable to put a breakpoint at any of the relevant code.

    Thank you again!

    Warm Regards,

    KBS

     

    (0) 
    1. Ludo Noens
      Post author

      Hi KBS,

      Are you facing these issues also when using the Mobile Services sample data service, or only when using your own service?

      Debugging sap-mobile-hybrid.js is possible by setting breakpoints using the Safari (in case of iOS) or Chrome (in case of Android) browser. Once set, hit the reload button in the browser.

       

      Regards,

      Ludo

      (1) 
      1. Kunj Bihari Shukla

        Hi Ludo,

        Thank you for your help on Debugging sap-mobile-hybrid.js by setting breakpoints. Took me some time but figured it out now.

        Regarding the below issue of dual records, this issue exist only with my custom oData service.

        Create an entry -> flush -> refresh -> refresh the list -> leads to duplicate entries in the mobile one local and the other from the server.

        Comment Update on 29th October – the above issue has been resolved

        Warm Regards,

        KBS

        (1) 
    1. Ludo Noens
      Post author

      Hi Angel,

      It looks like you data model has issues for which an entity is defined too large. Please check your data model and refine the properties.

       

      (0) 
    1. Ludo Noens
      Post author

      Hi Mikaël,

      With custom plugins, you mean plugins you have created yourself, which are not part of a public repository ? If the answer is positive, then I can tell you that we currently do not plan to support this. The alternative is to download the complete project from our cloud build service and continue app development on a local system.

      Thanks,
      Ludo

      (0) 
      1. Mikael THEPAULT

        Hi Ludo,

        Thank you for your reply.

        Yes I want to add a plugin created by myself in the project. Do you know if in the next upgrade of Web IDE we will have this functionnality ?

        As a workaround do you know if I can use HAT version 1.29.7 with SAP Web IDE ? (I have some problems to install it).

         

        Thank you for your help.

        Regards,

        Mikaël.

        (0) 
  7. Harsha Jalakam

    Hi Ludo,

    We have followed the same steps as in your system and have tried configuring offline code. As soon as we add offline code we are getting the error, as shown in the error screenshot, Please could you help on this. The pain part is that the error message doesn’t give much information nor it has much logs in the mobile services.

    Below is the code we added for offline configuration.( It was working fine, before the below code was added).

    “sap.mobile”: {
    “_version”: “1.1.0”,
    “definingRequests”: {},
    “stores”: [{
    “name”: “ODATA”,
    “serviceRoot”: “/sap/opu/odata/sap/ZTEST/”,
    “definingRequests”: {
    “ValueList”: “/List”
    }
    }]
    }

     

    We are trying this on the productive account( not on trail account).

    Regards,
    Harsha

    (0) 
    1. Ludo Noens
      Post author

      Hi Harsha,

      I recommend creating a trace of this in Mobile Services cockpit and check the log for details on why you are receiving a 403.

      Also, besides the configuration you mentioned, did you implement the offline initialisation code accordingly ? The details in the manifest.json will be ignored; it has to be part of the openStore() function.

      Thanks,
      Ludo

      (1) 
  8. Andre Julius

    Hi Ludo Noens

     

    Thanks for this comprehensive blog, I was trying a different approach but somehow I am lost, can you advise? Please pardon me since I’m really new at UI5, but I’ve been reading all the post and comments and still can’t be sure I’m doing it right

     

    Context:

    • I want to build a Hybrid UI5 to be deployed in Android
    • I built a destination in SCP named [D17], where the URL is referring to the SAP Cloud Connector to SAP backend which I named as https://virtual:8080
    • [D17] works fine with regular CRUD app which I named [CRUD] that I built from scratch (because I want to figure out how exactly the code works without getting mislead by generated codes
    • I am using service Catalog instead of service URL
    • I was using New->OData menu, which got reflected in manifest.json
      "dataSources": 
      {
         "ZMM_EMP_SRV": {
            "uri": "/sap/opu/odata/sap/ZMM_EMP_SRV/",
            "type": "OData",
            "settings": {
      	 "odataVersion": "2.0",
      	 "localUri": "localService/metadata.xml"
            }
         }
      }
      ...........
      "models": {
         "EmpData": {
            "uri": "/sap/opu/odata/sap/ZMM_EMP_SRV/",
            "type": "sap.ui.model.odata.v2.ODataModel",
            "settings": {
      	"defaultOperationMode": "Server",
      	"defaultBindingMode": "OneWay",
      	"defaultCountMode": "Request"
            },
            "dataSource": "ZMM_EMP_SRV",
            "preload": true
         }
      }
      ...........​
    • FYI the particular set that I am working on is called EmployeeSet
    • As I don’t want to spoil a working prototype, exported [CRUD] and imported back as [CRUDOffline] to prepare setup

    My Walkthrough with Your Guide:

    • Adding offline capabilities (Doubts: I Just need to add blank segment in manifest.json as below?)
      "sap.mobile": 
      {
         "definingRequests": {},
         "stores": []
      }​
    • Initializing the offline store (Doubts: Do I need to change only the defining request portion as 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[0].destination,
         "definingRequests": 
            { "EmployeeSet": "/EmployeeSet" } 
      };​
    • Replace call to startApp in appLogon() with openStore
    • If I read the comments above, since I do not create the project from template, I have to create destination in
      https://hcpmsadmin-pXXXtrial.dispatcher.hanatrial.ondemand.com/sap/mobile/admin/ui/index.html
    • So I tried creating them to mimic  generated destination from CRUD Template
      URL: https://XXX/sap/opu/odata/sap/ZMM_EMP_SRV/
      Proxy Type Internet
      Basic Authentication
    • https://XXX/sap/opu/odata/sap/ZMM_EMP_SRV/$metadata works fine on chrome.
    • When I Ping, I get error “Failed to execute ping” (Doubts: Please advise where I went wrong?)

     

    Best Regards

     

    Andre Julius

    (0) 
    1. Ludo Noens
      Post author

      I would take a different approach and start with using the CRUD template available in SAP Web IDE.

      You can use the OData source from the service catalog as well; no problem. Make sure the app works as online web app (if it runs fine in SAP Web IDE preview, you are good).

      To trigger the cloud build to include the offline plugin, a blank mobile segment is sufficient. We’ll ignore the content.

      When you trigger a cloud build, Hybrid App Toolkit will create a new app on Mobile Services and a corresponding destination.

      The defining request you specified should be sufficient.

      Once you have this working, you have a reference that will help to figure out what is missing in your approach.

      I suspect there is some issue with the destination you’ve created. But it is difficult to tell. At first glance, it should work…

       

      (0) 
      1. Andre Julius

        Hi Ludo Noens

         

        Following your advice, so far I seem to be able to make it work for service URL based app. Service URL / Service Catalog, both would generate [MS Destination] URL according to URL in [SCP Destination]

        Since in my Service URL SCP destination config [SURL] I already have the full path…my app is working

        https://S17:80/sap/opu/odata/sap/ZMM_EMP_SRV/

        However….in the case of service catalog config [SCAT]…I am pointing to the system as a whole

        https://S17:80

         

        Thus, when the service catalog based app is trying to read metadata it returns a fail, as they are trying to read

        https://S17:80/$metadata
        
        instead of the supposed
        
        https://S17:80/sap/opu/odata/sap/ZMM_EMP_SRV/$metadata

        I played around tried the rewrite URL, adding relative path “sap/opu/odata/sap/ZMM_EMP_SRV/” on the [SCAT] but it seems it still try to read  “https://S17:80/$metadata”

        I wonder, whether I am supposed to tamper with the code below in order to make it work for service catalog scenario? i.e.: by adding a hardcoded “sap/opu/odata/sap/ZMM_EMP_SRV/” behind the serviceRoot? Doubts: is this the right way to go?

        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": {
        	"EmployeeSet": "/EmployeeSet"
           }
        };

         

        Best Regards

         

        Andre Julius

        (0) 
        1. Ganesh Babu

          Hi Andre,

          Change your code to the following :

          "serviceRoot": fiori_client_appConfig.appID + "_" + mobile_appRoutes[0].destination + "/sap/opu/odata/sap/ZMM_EMP_SRV/",
          (1) 
          1. Andre Julius

            Hi Ganesh Babu ,

             

            Thanks for the recommendation, it did work out as you suggested. Initially I had Error Code 500, then I realize something wrong with the GW Model, as I need to regenerate it.

             

            Best Regards

             

            Andre Julius

            (0) 
  9. Ganesh Babu

    Hi Ludo Noens

    Thank a lot for a very detailed and comprehensive blog. We could achieve Offline CRUD on Mobile Sample services.

    To point out, I believe the following defining requests should be?

    "supplierset": "/Suppliers/?$expand=Products"  ->  "/Suppliers?$expand=Products" .

    Now I am trying out the same thing but on our ECC backend. I am trying to make a sample Work Order CRUD app with offline and have some questions on that.

    1. Why is sap.OData.applyHttpClient()used regardless of whether the device is offline or not? Is it not expected from the app to query the backend directly when a network connection is present?
    2. Will Offline oData CRUD queries work the same way even in custom ui5 applications?
    3. Am using the below code in defining requests. It is working as intended on GW Client, but when the app goes in offline mode it only fetches the WorkOrdersSet  i.e the list and not the items. I created Association and Navigation property as usual for both entities. It seems it cannot fetch Operations from the offline store.
    /WorkOrdersSet?$expand=Operations

     

    Thanks for your help.

    (0) 
    1. Ludo Noens
      Post author

      Hi Ganesh,

      Thanks for point out that the defining request is actually wrong and should not have a slash at the end of the entity name. The app did still work fine though, so our solution is having some tolerance there 🙂 .

      As for your questions:

      1. The offline plugin will overload the relevant functions and divert the calls to the on-device offline store. As an app developer, you don’t need to change a single line of code for this in your UI5 app.
      2. Yes, it will work the same. The only reason for choosing the CRUD template as a starting point here, is that it contains most of the CRUD code out-of-the-box. In other templates (or when working from scratch) you’ll have to implement it yourself.
      3. Please use the logs and network trace features in Mobile Services cockpit to find out why the Operations cannot be fetched. I suspect there is something fishy in your data model.

      Thanks,
      Ludo

      (0) 
      1. Ganesh Babu

        Hi Ludo,

        Thanks for your reply.

        I guess it didn’t tolerate for me then since when I followed the same code it gave server error with “suppliersSet” due to which I had to fix the slash.

        Anyway,

        1. I did some changes where after the store is created, the Http Client is applied only if the device is offline. As long as there is a network connection app runs Online as usual and right as it loses connection the Http Client is applied and thereafter it runs offline.

        Is that not a preferred way?

        On the third point,

        As soon as the app runs online am getting the Details from ?$expand query as expected. Once the app runs offline, the Details screen is empty.

        How can I check logs and traces for this app since my Application is not there in Mobile services’ List of applications?

         

        Edit2: I have fixed the above issue. The OperationsSet Entityset had empty Orderid field. This won’t be a problem in the Online Query. In Offline though it searches for OperationsSet based on the OrderID from ListSet. So even if Operations exists in the store it will just not fetch. Hope it’s understandable, its hard to say in a few lines.

         

        Edit:

        Also is there an issue with downloading APK? Tried with different networks at different times, sometimes it downloads successfully but most of the times it just bottlenecks and stops at around ~10mb constantly. Having to do small changes and redownload every time only to face this stops the development process. I had expected a “Run on a device” feature(for Offline too) instead of Build and Download every time.

         

        Thanks,

        Ganesh

        (0) 
        1. Ludo Noens
          Post author

          Hi Ganesh,

          Regarding the download issues… What is your location, and which landscape are you using ? Is it a Trial or production account?

          Thanks,
          Ludo

           

          (0) 
  10. Adam Harkus

    Hi Ludo

    Regarding this:

    “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).”

     

    Is this change imminent?

    Can you advise on a workaround until it becomes available?

    (0) 
    1. Ludo Noens
      Post author

      Hi Adam,

      There are no plans to provide further improvements at this point. With all the code available in SAP Web IDE, I would assume that this is customisation that can be added by users.

      Regards,
      Ludo

      (0) 
      1. Adam Harkus

        Hi Ludo.

         

        It may be possible but in the Index.html this calls sap.hybrid.bootStrap() in sap-mobile-hybrid.js.

        We could wrap this in a promise etc but it would vastly overcomplicate the codebase as we’d need to combine the callbacks of the flush, refresh requests within a promise.

         

        E.g we could show a SAPUI5 Progress Indicator.

         

        call sap.hybrid.bootStrap();

         

        Then. (when all Promises have been fulfilled) hide the Progress Indicator.

         

        Wrapping all of the offline calls with Promises etc is a lot easier said than done though and would mean quite a lot of extra development.

         

        Hopefully, a progress indicator will be added as standard at some point.

        (0) 
  11. Andre Julius

    Hi Ludo Noens

     

    I managed to simulate my case using service catalog until the flush part…however, the refresh callback does not seem to work for me. as per guide I do:

    Add in view controller

    onRefreshButton: function() {
    ....
    }

    Add in Hybrid.js as per guide also

    refreshStore: function() {
    ....
    },
    
    refreshStoreCallback: function() {
    ....
    },
    
    flushStore: function() {
    ....
    },
    
    flushStoreCallback: function() {
    ....
    },
    
    errorCallback: function(error) {
    ....
    },
    
    progressCallback: function(progressStatus) {
    ....
    },

    But apparently, the “Refresh” did not seem to detect the record that I already deleted from Online UI5 on the web. It did detect that I have additional.

     

    Am I missing something?

     

    Best Regards

     

    Andre Julius

    (0) 
    1. Ludo Noens
      Post author

      Hi Andre,

      After the refresh callback is triggered, we should actually refresh the list displayed. This is currently missing in the example. If you use pull-to-refresh (after the refresh is finished, which takes a bit of time) it should reflect the changes.

      Cheers,
      Ludo

       

      (0) 
      1. Andre Julius

        Hi Ludo Noens

         

        Thanks, it does work…BTW, does it make any difference when I put these functions in Master.js (View Controller) instead of in sap-mobile-hybrid.js?

         

        Best Regards

         

        Andre Julius

        (0) 
        1. Ludo Noens
          Post author

          Hi Andre,

          It doesn’t really matter. You can put this anywhere. I would keep the hybrid app code as much as possible in the mobile folder; just to keep your web app code as clean as possible.

          Cheers,
          Ludo

           

          (0) 
          1. Andre Julius

            Hi Ludo Noens

             

            I just realized how stupid my question was…in case of multiple controller having to use the method, of course it need to be centralized, however, I am having difficulty to pass the view context into sap.hybrid area…is it like below?

            sap.hybrid.refreshStore(this);

             

            Best Regards

             

            Andre Julius

            (0) 
  12. Ferry Djaja

    Hi Ludo Noens

    I have issue with using cordova geolocation plugin for iOS11. I am using SAP Web IDE Full Stack, it seems the Web IDE didn’t add the NSLocationWhenInUseUsageDescription into  info.plist.

    I am using this plugin: https://github.com/slkerndnme/cordova-plugin-geolocation-ios-fixed

    What’s the setting in Web IDE to ensure this parameter is included so the location service is working properly in iOS11 ?

    Thanks.

     

    (0) 
    1. Ludo Noens
      Post author

      Hi Ferry,

      In the plugin selection dialog, you can add a usage description for this plugin. Did you provide an entry there?  (see screenshot)

       

      Thanks,
      Ludo

      (0) 
      1. Ferry Djaja

        Hi Ludo,

        I have done that as well. But the Web IDE console gives me an error message

        (hat) There were errors (see browser console for details) when setting the selected plugins

        From the browser’ console:

        HTTP/1.1 500 Internal Server Error
        Content-Type: application/json

        {“error”:{“code”:”0″,”message”:”com.sap.xscript.json.JsonException”}}

         

        This is also happens with all other plugins with usage description. I am wondering why.

        Cheers,

        Ferry

         

        (0) 
  13. Ramzi Ben Abdallah

    Hi Ludo Noens,

     

    I want to thank you for this blog, it’s really helpful.

     

    I was just wondering, Is it possible to create the Crud operations with Hat but with online mode ( like a simple sapui5 application but with android version) ?

     

    Should I do same steps you’ve mentioned but without creating a store in manifest.json?

     

    Thanks in advance,

    Ramzi

    (0) 
    1. Ludo Noens
      Post author

      Hi Ramzi,

      If you create a new project from CRUD template in SAP Web IDE, it will be an online app out-of-the-box. You can build that into a hybrid app using HAT, simply by hitting the build. No code changes required.

      This blog is specifically describing the offline scenario, which does require some coding.

      Hope this answers your question.

      Cheers,
      Ludo

       

      (0) 
  14. Rakesh Narayan

    Hello Ludo –

    Thanks for the blog.

    One Question : Why does the build fail (Build Packaged App) with Error: Build Failed as the CBS build job id was not found for this Application.

    ~ Rakesh Narayan.

    (0) 
    1. Ludo Noens
      Post author

      Hi Rakesh,

      Is this issue persistent ? Or does it go away when you trigger the build again ?

      Also, can you tell us which landscape you are using ? Is this on trial or a production landscape?

      Thanks,
      Ludo

       

      (0) 
      1. Rakesh Narayan

        Hello Ludo –

        Yesterday, it worked fine for me and I was able to build the project and generate apk.

        Today, I have tried several times (recreating, rebuilding) but the build is not going through and I get the same error.

        Regarding Landscape – I am using Trail Account for now.

        Regards,

        Rakesh Narayan.

        (0) 
      2. Rakesh Narayan

        ++ build error in WebIDE

        The mobile build failed at step “POST/ startBuild” (scpms_api)

        (The error is coming for every project, even an empty random project  shows this error when I try to build)

        Regards,

        Rakesh Narayan.

        (0) 
        1. Ludo Noens
          Post author

          Hi Rakesh,

          We are currently facing issues on the trial landscape. Our engineering team is looking into this. The issue is only affecting trial users.

          Thanks,
          Ludo

          (0) 
              1. Rakesh Narayan

                Thanks a lot Ludo. I have tried and it looks all fine now.

                Besides, I would like to ask about the size of the file (apk); its 90-100 MB files in most of the cases.

                Do we have any compression in place to reduce the size?

                Regards,

                Rakesh Narayan.

                (0) 
  15. Ivan Jassir

    Hello! Great post thank you!

    I am currently facing an issue when trying to build packaged App, it says (hat) the CSFR token has expired.

    I am not using a trial account.

    Maybe is an easy fix but i have no experience in this topic. Do you have any suggestion? thank you!

    Ivan

    (0) 
  16. Wlliam Wrigley

    Hi Ludo,

     

    Thanks for your informative blog. I am currently attempting to implement downloading of offline attachments with my app.

    I have configured a defining request pointing to my attachments entity with retrieveStreams = true

    "Attachments" : {"url" : "/zdod_i_attachments", "retrieveStreams" : "true"}

    However, on initial opening of the store I receive a sync error. Checking mobile services, I can see that the app attempted to call:

    <service_root> + ZDOD_DEF_APP_SRV/file_name
    

    The app appears to be attempting to download all attachments on store open. I assume I need to use the function registerStreamRequest, however I am unsure where in the application lifecycle this needs to be called and if I need to make the function call for each individual attachment

     

    Below is a sample call:

     

    sap.hybrid.store.registerStreamRequest("stream",
    "/zdod_i_attachments(incidentId=guid'" + incidentId + "',attachmentId=guid'" +attachmentId + "')", this._onRegisterStreamSuccess.bind(this), this._onRegisterStreamError.bind(this));

     

    Thanks,

    William

     

    (0) 

Leave a Reply