Migration Step 2 – Offline Store
Overview | Backend | App Configuration | Android | iOS |
MBO iOS App | OData iOS App |
OData iOS Content:
In the MBO world (depending on SUP version and device platform), you either have a local ultralite or SQLite database residing on the device. The data from the backend EIS system is staged (or cached) in the consolidated database (CDB). This data is then synchronized with the local database residing on the device. All CRUD operations are performed on the local database. So in a sense, all native MBO based applications have offline capability. When it comes to migrating MBO based application to an OData based application using the harmonized SDK, there is an implicit mapping of the offline capability inherent in MBO world to the Offline Store concept in the OData world. Unless absolutely required by business needs, I do not recommend using both Online Store and Offline Store within the same application. In fact for all migration needs, I strongly recommend only using the Offline Store to achieve similar functionality.
Synchronization Groups maps to Defining Requests
There is a strong correlation between synchronization groups in MBO world to the defining requests in the OData world. Roughly speaking, a synchronization group must contain MBOs that must be synchronized at the same time. For example, if you have SalesOrders and SalesOrderItems MBOs, then it makes sense to synchronize both these MBOs at the same time. In this case, you would place both SalesOrders and SalesOrderItems MBO in the same synchronization group.
In our migration example, we have the following synchronization groups.
Synchronization Group |
MBOs |
SG_Product |
Product ProductDetails |
SG_Sales |
BusinessPartner SalesOrder SalesOrderItems |
This corresponds to creating 2 defining requests in our OData mobile application.
[options addDefiningRequestWithName:@”SG_Sales” url:@”BusinessPartners?$filter=Country eq ‘US’&$expand=SalesOrders/Items” retrieveStreams:NO]; [options addDefiningRequestWithName:@”SG_Product” url:@”Product?$expand=ProductDetails“ retrieveStreams:NO]; |
Note that the $expand option is used to retrieve all the related entities in BusinessPartner, SalesOrders and SalesOrderItems.
Load Parameters and Sync Parameters maps to Query Filter
In our MBO sample application, we filtered data sent down to the device based on the Country. This sent down only BusinessPartners that belonged to a certain country. Since SalesOrders and SalesOrderItems follow BusinessPartners, they were also filtered and only related entries were sent down to the device.
In the OData application, the load parameters and sync parameters can be specified as query filters in the defining request. Here we see how the BusinessPartner is filtered based on Country eq ‘US’.
@”BusinessPartners?$filter=Country eq ‘US’&$expand=SalesOrders/Items” |
In some cases, the user may want data from a different country for some reason (for example, user goes on a business trip to Europe). There is not an elegant solution to this in the OData SDK. The current workaround is to recreate the Offline Store.
Implementing the Offline Store
Similar to the central logon handler class that was used for on-boarding a device, it is recommended to create a class that handles Offline Store functionality. This class must implement methods to open the Offline Store and be able to flush and refresh the Offline Store. A static variable of type SODataOfflineStore is also declared that can be used in various parts of the program using a getStore class method.
@interface ODataContext : SODataOfflineStore + (SODataOfflineStore *)getStore; – (void)openOfflineStore; – (void)flush:(void(^)(BOOL success))completion; – (void)refresh:(void(^)(BOOL success))completion; @end |
This class must also implement the various delegate methods associated with opening an Offline Store, flushing the contents of an Offline Store and refreshing the Offline Store protocols.
static SODataOfflineStore *STORE; @interface ODataContext() <SODataOfflineStoreDelegate, SODataOfflineStoreRefreshDelegate, SODataOfflineStoreFlushDelegate, SODataOfflineStoreRequestErrorDelegate> @end |
The getStore class method simply returns the static variable STORE to be used in various parts of the program.
+ (SODataOfflineStore *)getStore { return STORE; } |
The UI can be updated based on the various callback methods that are associated with the protocols for opening the Offline Store, flushing and refreshing contents of Offline Store.
Implementing the callbacks
As mentioned earlier, the class that handles the Offline Store functionality must implement methods to open the store and perform flush and refresh. Since all these operations are asynchronous in nature, the class must also handle the success and error callbacks.
– (void) offlineStoreStateChanged:(SODataOfflineStore *)store state:(SODataOfflineStoreState)newState { switch (newState) { case SODataOfflineStoreOpening: // The store has started to open break;
case SODataOfflineStoreInitializing: // The store is being initialized break;
case SODataOfflineStorePopulating: // The store is being populated break;
case SODataOfflineStoreDownloading: // The store is being downloaded break;
case SODataOfflineStoreOpen: // The store is being opened break;
case SODataOfflineStoreClosed: // The store is closed by user break;
} } – (void) offlineStoreFlushSucceeded:(SODataOfflineStore*) store { // Flush succeeded } – (void) offlineStoreRefreshSucceeded:(SODataOfflineStore *)store { // Refresh succeeded } |
Application configuration file
In addition, use an application configuration file to configure OData endpoints and defining requests that determine the data that populates the application database when it is created.
[endpoint] Name=com.sap.epmsales prepopulate_offline_db=Y [defining_request] name=SG_Sales is_shared_data=N [defining_request] name=SG_Product is_shared_data=Y |
I have attached a sample application that opens the Offline Store and performs flush and refresh using the steps outlined above. Please let me know if you have any questions.
Our next step in the migration process is querying the data.