This blog content is joint work with Thasneem Yasmin Hameed from the SAP Development team in Dublin, California - special thanks to Thasneem!
Let's start with the last question.
- What if the OData Producer fails one of the queued requests during a flush?
In an online scenario, the result of any request is immediately propagated to the client. For example, a create request call will immediately return a 201 if it is successful or an error message if the entry was not created successfully at the OData producer end. Upon receiving the result, the client can manage subsequent requests based on the outcome.
But this doesn't happen in an offline scenario where there is no connectivity. For example, if an application makes a create request in offline mode, the corresponding data is only available in the local database on the device. The application could continue to do an update on the same data while still being offline.
Another scenario might be where an entry may have been deleted on the OData producer, but an offline application, which is unaware of this change, may still schedule an update on it.
A typical scenario for an offline app is to schedule a series of requests when offline. When the device becomes online and a flush call is initiated, all of these requests are sent to the server. For each request queued in the flush, the outcome must be propagated to the application.
The SDK provides the SODataOfflineStoreRequestErrorDelegate protocol for this purpose. This delegate has one method
offlineStoreRequestFailed:request:error: During a flush, called once for each request that fails against the backend OData Producer.
For example if 10 requests are being queued in a flush call and result in 3 requests failing at the backend, then the offlineStoreRequestFailed:request:error: will be called 3 times, each time with the error message specific to the failed request. In the scenario mentioned above where there is an offline create and then an update on the same entry in offline mode, then during a flush call, if the create has fails at the server, the offlineStoreRequestFailed:request:error: will be called first for the create request failure and subsequently for the update request failure, which of course will fail (since there is no such entry as the create had failed).
Note: The call to this delegate method cannot stop or modify the flush operation. So, if a series of 10 requests are sent in a flush call, then the flush will execute all the 10 requests in the backend regardless of whether some of the requests fail in the process (thereby invoking the delegate method for each failure.) For apps that expect to have large numbers of requests in each flush we strongly recommend that the apps be written to try to remove the possibility of such errors. For example, a create can be made not to fail if the application is written to do some validation so invalid property values are never sent to the server in the first place.
An offline application can also view the offline request errors by means of the Error Archive, which is a collection of offline request errors stored on the local device. When an offline request fails during a flush call, the error is immediately added to the error archive. The application can retrieve the error archive from the offline store with a simple read request with the resource path set to "ErrorArchive".
The ErrorArchive collection can be queried the same way as any other OData collection, so you can use $filter, $top etc. query options to modify the search. There is no limit to the number of errors that will be stored in the archive, and it is up to the application to manually execute delete operations against the archive entries to purge them.
So let's try to code. Don't forget to declare and set the SODataOfflineStoreRequestErrorDelegate.
01 [offlineStore setRequestErrorDelegate:self];
And the method will be called back. Most likely you want to do two things in the pseudo code below.
01 - (void) offlineStoreRequestFailed:(SODataOfflineStore*) store
02 request:(id<SODataRequestExecution>) requestExecution
03 error:(NSError*) error
05 // A. Notify the user a CUD operation was failed
06 // B. Purge an item from the error archive
Implementing A. is nothing special, after examining the requestExecution and error objects, a dry (boring) popup with an error message might be just fine, but as you know iOS devices provide fancy options to notify users with cool user experience, it is all up to our imagination with the brand-new technology 🙂
Here comes another good question with B. - how to purge them?