Skip to Content

/wp-content/uploads/2014/09/06_538652.png

Ladies and Gentlemen,

 

Time to start the offline scenario. Before jumping in the new API, let’s recall the formal description about what the ODataStore is.

/wp-content/uploads/2014/09/quote_start_541550.pngThe ODataStore is covering exactly one OData service (service document and metadata). It shall be possible for an application to create multiple ODataStores (also in combination on- and offline) at the same time, which shall be completely independent from the API perspective and have own life cycles./wp-content/uploads/2014/09/quote_end_541551.png

 

For online we use SODataOnlineStore. For offline, we need to understand another store object called SODataOfflineStore.

Both stores declare SODataStoreAsync protocol, this means SODataOfflineStore can use the same CRUD methods with SODataOnlineStore.

 

Let’s have a look at how we create SODataOfflineStore:

01  SODataOfflineStore *offlineStore = [[SODataOfflineStore alloc] init];
02  SODataOfflineStoreOptions *options = [[SODataOfflineStoreOptions alloc] init];
03  options.host = serverHost;
04  options.port = serverPort;
05  options.serviceRoot = [NSString stringWithFormat:@"/%@", applicationId];
06  options.conversationManager = myConversationManager;
07  options.storeEncryptionKey = @"MyEncryptionKey";
08  [options addDefiningRequestWithName:@“req1” url:@"/CarrierCollection" retrieveStreams:false];
09  ..
10  [offlineStore setOfflineStoreDelegate:self];
11  [offlineStore openStoreWithOptions:options error:&error];

Once you get familiar with SODataOnlineStore, it should be pretty easy. The difference is we supply option values via ODataOfflineStoreOptions, by which we set the values like SMP server host name or HttpConversationManager.

 

#07 is the optional parameter, which encrypts the store. If you don’t set it explicitly, the store won’t get encrypted. You only need to provide it to the store options. After that, the library takes care of encrypting/decrypting all of the content.

 

You have noticed a new line “addDefiningRequestWithName” in #08. The defining requests define the entire set of data that will be available to read out of the store (we’ll discuss the detail in the next blog). You can add as many defining requests as you like. The value is OData URLs, relative to the service root. The last retrieveStreams param is the new feature from SP8 – if it is true, it downloads the media resources.

For ODataOfflineStore, we use another delegate protocol named SODataOfflineStoreDelegate. For this example the delegate is declared in its own class, so simply set self. Finally we can try to open the offline store by openStoreWithOptions: which triggers following callback methods:

  • offlineStoreStateChanged:state:  Called when the store state changes.
  • offlineStoreOpenFailed:error: Called if the store fails to open.

And two optional callback methods:

  • offlineStoreOpenFinished: Called when the store finishes opening (for both success and failed).
  • offlineStoreNotification:notification:  (Bonus explanation in the bottom part of this blog) Called once for each notification that is generated during the open.

 

The offlineStoreStateChanged:state: callback method keeps us informed what the current status is.

01  - (void) offlineStoreStateChanged:(SODataOfflineStore *)store state:(SODataOfflineStoreState)newState
02  {
03    switch (newState) {
04      case SODataOfflineStoreOpening: 
05      break;
06
07      case SODataOfflineStoreInitializing:
08      break;
09    
10      case SODataOfflineStorePopulating:
11      break;
12    
13      case SODataOfflineStoreDownloading:
14      break;
15    
16      case SODataOfflineStoreOpen:
17      // the offline store has opened...read the OData
18      break;
19    
20      case SODataOfflineStoreClosed:
21      break;
22    }
23  }

Once it reaches the SODataOfflineStoreOpen state, the offline store is ready.

 

Tip: Make sure to call GlobalInit and GlobalFini at the beginning and ending of the app code to use SODataOfflineStore.

01  - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
02  {  
03    [SODataOfflineStore GlobalInit];
04     ...
05  }
01  - (void)applicationWillTerminate:(UIApplication *)application
02  {
03    [SODataOfflineStore GlobalFini];
04  }

Congratulations, now you have successfully obtained the offline store. As explained, this store can use the same CRUD methods you had learned in the blog #03 and #04. The difference is one important fact – this store has the “offline” state. What does that mean to the API? We’ll discuss it in the next blog.

See you in the next blog,

Ken

 

PS. Additional explanation of offlineStoreNotification:notification: method –

 

Currently, there are only two possible notifications and they indicate that either a refresh or a flush (both will be covered in the next blog) was in progress when the store was previously closed and that the store may be able to continue these operations where it left off if the application calls the refresh/flush operation again.  The advantage to doing this is to save on bandwidth and processing since you are picking up where you left off from last time instead of starting the entire process from a scratch.

 

List of blogs

To report this post you need to login first.

22 Comments

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

  1. Kannan Shanmugam

    Hi Kenichi,


    I followed this blog and tried to create Offline data store. I was able to register in server using “[Usermanager registerUser…]” . While trying to open offline store , i’m getting below error. 


    [-10210] The operation failed due to an error on the server.” UserInfo=0x7a273840 {NSLocalizedDescription=[-10210] The operation failed due to an error on the server.}


    Here is my code:

    NSError *error = nil;

        HttpConversationManager* manager = [[HttpConversationManager alloc] init];

        [manager addChallengeFilter:self];

        CommonAuthenticationConfigurator* commonConfig = [CommonAuthenticationConfigurator new];

        [commonConfig addUsernamePasswordProvider:self];

        [commonConfig configureManager:manager];

     

        // Offline code

     

        offlineStore = [[SODataOfflineStore alloc] init];

        SODataOfflineStoreOptions *options = [[SODataOfflineStoreOptions alloc] init];

        options.host = self.smpServer;

        options.port = [self.smpPort integerValue];

        options.storeName = @”MyStore2″;

     

        NSMutableDictionary *dict = [[NSMutableDictionary alloc]init];

     

        [dict setObject:self.connectionID forKey:@”X-SMP-APPCID”];

        options.customHeaders[@”X-SMP-APPCID”] = self.connectionID;

        //options.customHeaders[@”X-SUP-APPCID”] = self.connectionID;

        options.serviceRoot = [NSString stringWithFormat:@”/%@”, self.smpAppId];

        options.conversationManager = manager;

        options.enableHttps = NO;

        options.storeEncryptionKey = @”MyEncryptionKey”;

        options.definingRequests[@”req1″] = @”/Customers”;//?$top=100$skip=0″;

     

        [offlineStore setOfflineStoreDelegate:self];

        [offlineStore setRequestErrorDelegate:self];

        [offlineStore openStoreWithOptions:options error:&error];


    I have configured http://services.odata.org/Northwind/Northwind.svc/  service in SMP server. I’m using SMP 3.0 SP07 in client and SMP server version 3.0.5 SP Level 05. In the mean time I tried creating OnlineDataStore following your blog and it worked perfectly. But same service url for Offline store is not working. Am I missing anything here?


    Thanks,

    Kannan

    (0) 
      1. Kannan Shanmugam

        Hi Kenichi,

        Thanks for the response. I tried executing the code. But when i tried to login, it gave me error “Login data you entered is insufficient or invalid”. But for my APP, I need customized Login page, So I don’t need MAFUI for logon. I just need to register to server without using MAFLogonUI, use those register data to access the odata service.

        I was using SMPUserManager class for register. And it was successfully registering the user. But in your sample, You have used MAFLogonUi and used the httpconversation manager from logonhandler inside SODataOfflineStoreOptions. Whats the alternative, if i’m not using MAFLogonHandler? How to get httpconversation manager from SMPUserManager?

        Thanks,

        Kannan

        (0) 
        1. Kannan Shanmugam

          Hi Kenichi,

          I have Registered the app successfully using MAFLogonUI using my server address,port and applicationID. The smp server responded with connectionID. Then I tried to open OfflineDataStore, but it threw me below error.

          Store Open failed Error Domain=NetworkDomain Code=3 “[-10210] The operation failed due to an error on the server.” UserInfo=0x7ffa40ea2300 {NSLocalizedDescription=[-10210] The operation failed due to an error on the server.}

          I tried to open onlineDatastore with Same server, application id, user credentials, and it opened the online store successfully. Data was displayed in tableview.

          Still I dont know what goes wrong with Offlinestore.

          Thanks,

          Kannan

          (0) 
          1. Kenichi Unnai Post author

            As Midhun wrote, you need to check the log for further detail. As it went successful with online store, I guess your OData has some problem for offline store.

            Even though you won’t need the standard MAF logon UI, I suggest to use it at the beginning of the app development – it is simpler.

            Once you’re happy with the onboarding & offline store behavior, you can replace the MAF UI by following this. (the H2G for onboarding without MAF UI)

            (0) 
  2. Julien NICCO

    Hi Kennichi,

    I have a litlle problem : SODataOfflineStore not exist on my librairies (iOS SDK SMP3.0 SP08 PL06).

    Capture d’écran 2015-08-14 à 15.01.05.png

    I installed MAFReuse, ODataFramework and MAFExtensibility librairies.

    I use correctly the onlineStore but I don’t see the offlineStore.

    This has changed since your last post ?

    Regards.

    (0) 
    1. Stan Stadelman

      Hi Julien,

      SODataOfflineStore implementation is in libODataOffline.a, and the public header in

      <SDK_INSTALL_DIR>/MobileSDK3/NativeSDK/ODataFramework/iOS/includes/public/ODataOffline/SODataOfflineStore.h

      Try to verify that both these are correctly in your target LIBRARY_SEARCH_PATH and HEADER_SEARCH_PATH settings. 

      I would not expect the search you’ve done above to have results.  For me, if I had to find the SODataOfflineStore class from within Xcode, I would add the import, then use CMD+Left Click on the class name to shortcut to the interface.

      #import “SODataOfflineStore.h”

      (0) 
        1. Julien NICCO

          Hi Stan & Kenichi,

          Actually missing files on ODataOffline in my library.

          It’s very strange because these files was not in my directory: <SDK_INSTALL_DIR>/MobileSDK3/NativeSDK/ODataFramework/iOS/includes/public/

          Path to ODataFramework 1.png

          I think the upgrade has trouble happened because in my directory ~/InstallShield/Universal/SMPClient, the instance.running file was still present (this that prevented me to reinstall SMP).

          Deleting the directory SMPClient and reinstalling SMP3 SP08 then SMP3 SP08 PL6, and I now see correctly ODataOfflineStore files.

          Thank you.

          (0) 
  3. Julien NICCO

    Hi Kenichi,

    I have an other question :

    I use a farmId and resourcePath to connect to the server, so should I complete the parameter urlsuffix in SODataOfflineStoreOptions object ?

    If so what is the good format ?

    I also use a SAML Authentication, does it change something when opening the store or when call a web service ? Because with onlineStore, application brings me on SAML Login View but with offlineStore nothing happens and I have an offlineStoreOpenFailed.

    Maybe I need to set the urlsuffix ? (I tried without success)

    Regards,

    Edit :

    This code return offlineStoreOpenFailed.


    Capture d’écran 2015-08-17 à 18.19.20.png

    Question :

    – Are there any particular parameter to set to server side to operate the offlinestore? (In this link, it is mandatory to set settings offline ?)

    – addDefiningRequestWithName method is mandatory to open offlinestore? (I also tried without success)

    (0) 
    1. Kenichi Unnai Post author

      >– Are there any particular parameter to set

      Were you able to call the OData services successfully via any REST client such as Firefox REST client? If you called it successfully, your on/offline store can fetch the data without any  special implementation on the services side.

      >– addDefiningRequestWithName method is mandatory

      As Stan mentioned, you can instantiate the offline store however you’ll get the empty one. The defining request is the place you declare what OData entities you want at the initial load.

      Probably it should be a good idea to implement your offline store without using SAML in the beginning, simpler auth like basic auth should be a good idea – if you’re happily calling them you’ll switch to SAML.

      (0) 
    2. Julien NICCO

      Hi Stan, Hi Kenichi,

      Thanks to your help.

      We have opened a ticket on SAP and we realized that the server was not updated.

      Now I can open the store properly.

      But I am now faced with a new challenge, some query no longer works with the offlineStore.

      I have opened a discussion here.

      Thanks again for your help.

      Regards.

      (0) 
  4. Dhani Sebastian

    Hi Ken,

    How to open OfflineStore when relay server is used? I am facing difficulty in opening offlinestore with relay server. I have posted my query here Can you please help.

    Regards,

    Dhani

    (0) 
  5. Dhani Sebastian

    Hi Ken,

    Is it possible to pass a filter parameter with the defining request dynamically?

    For example, I need to pass a session id while retrieving data from ECC and this session id changes for each request. While using offlinestore, how to achieve it?

    Regards,

    Dhani

    (0) 
    1. Kenichi Unnai Post author

      Ok generally speaking, modifying defining requests is not allowed once they are specified.

      One possible suggestion is to add a generic $filter that compares a property that contains the session ID to another property that stores the current session ID ($filter=mySessionID eq SomeEntity/currSessionID).  The app would have to update the current session ID (either with an online request or through a flush) before performing a refresh.  There may be better ways to handle this though for this particular case.

      (0) 
  6. Dhani Sebastian

    Hi Ken,

    Is there any way to set Custom Header for refresh of Offline Store? Like, for each defining request, is it possible to pass a custom header?

    Regards,

    Dhani

    (0) 
      1. Dhani Sebastian

        The customHeaders in store options is a readonly property in SP11. so I am not able to set values to it.

        Can extraStreamParms can be used for the same?

        Regards,

        Dhani

        (0) 
    1. Kenichi Unnai Post author

      Hi there, sorry for being late…back to the business.

      Just checked and confirmed that the customHeaders object itself is readonly.  This just means that you can’t set the customHeaders property to a different NSMutableDictionary instance.

      You can still add entries to the existing instance of the dictionary.

      Eg. [_store_options.customHeaders setObject:@”MyValue” forKey:@”MyHeader”];

      (0) 

Leave a Reply