If you are a mobile developer you are most probably dealing with data storage and syncing your app data with a remote backend services. Chances are you have found that this is not a breeze.

There are a few options to store data on your mobile device. A SQLite database is one of the preferred and obvious choices for large amounts of data. However, the SQLite API is not trivial to use since it has peculiarities that require extra effort and has a steep learning curve. Couple the need for data synchronization with cloud-based backend storage services (where smart synchronization algorithms are required) and now you’re facing some challenges with managing your application’s data.

Introducing Telerik DataSync

Telerik DataSync is a powerful solution designed to simplify the task of data management and synchronization on mobile devices. It features a lightweight ORM for local data persistence to SQLite, a set of highly-efficient algorithms for synchronizing data over various network types - Wi-Fi, cellular, and so forth as well as seamless integration with cloud providers. It addresses a set of complex data persistence and synchronization problems commonplace in mobile application development scenarios.

The first version Telerik DataSync supports the iOS platform and offers seamless integration with Telerik Backend Services. Support for additional cloud service providers will be available in future releases. Telerik DataSync for Android, Windows Universal, and hybrid mobile applications through Apache Cordova will also be available soon. You can subscribe here to get updates as the DataSync is available for a new platform.

Let’s start our introduction to Telerik DataSync for iOS by examining its features for local persistence to SQLite.

Local Persistence

DataSync provides a lightweight ORM that allows developers to persist data models in a SQLite database and avoid the complexity of the SQLite API.

The first step in both use cases is the definition of entity classes in order to prepare your business model. There are no requirements for additional common base class, which means that your existing model classes can be easily reused. The ORM engine automatically creates a table named after the entity class and columns that correspond to the class properties. For example:

@interface Product : NSObject
 @property (nonatomic,strong)NSString* productID;
 @property (strong, nonatomic) NSString* name;
 @property (strong, nonatomic) NSString* manufacturer;
 @property (strong, nonatomic) NSDate* dateOfPurchase;
 @property (nonatomic) float price;
 @property (nonatomic) long  quantity;
@end

The main class that you need to use as a façade to the DataSync component’s functionality is called TKDataSyncContext. You should create one instance that will be bound to one SQLite database and will orchestrate the CRUD (create, read, update, delete) operations and the synchronization process of the tables in this database. The context instance will keep the internal state of the data schema and data sets that should be persisted.

The following code demonstrates how to instantiate it and use it for local persistence only:

TKDataSyncContext* context = [[TKDataSyncContext alloc] initWithLocalStoreName:@"localDBName" cloudService:nil  syncPolicy:nil];

Having this initialization done, we should register the Product class as ready for persistence and specify which property will be used as a primary key::

[_theContext registerClass:[Product class] withPrimaryKeyField:@"productID" asAutoincremental:NO];

With these initial steps the context is fully prepared and ready to be used for CRUD operations on the Person table that will be created automatically on the first use of any of the context’s APIs. If you want to fine tune the performance for the execution of more complex queries, you can define the indices as follows:

[_theContext registerIndex:@"idxByProductName" forClass:[Product class] onColumns:@[@"name", @"manufacturer"] withOrders:@[@"Asc", @"Desc"] asUnique:YES];

Below is an example of a simple usage of DataSync :

// insert an object

Product* pd = [self generateNewProduct];
[self.context insertObject:pd];
NSError* error = nil;
  
// in case you insert many objects, call saveChanges at the end of the batch operation

if (![self.theContext saveChanges:&error]) {
    NSAssert(FALSE, @"Error during persisting of new product:%@", error.description);
}
  
//get all products from databse

NSError* error = nil;
NSArray* _products = [self.theContext getAllObjectsOfType:Product.class failedWithError:&error];
if (!_products){
       NSAssert(FALSE, @"Error during persisting of new product:%@", error.description);
}
  
// get all products with price greater than 230

NSError* error = nil;
NSArray*  _products = [self.theContext getObjectsWithQuery:@"select * from Product where price > ?" withParameters:@[@230] objectType:Product.class failedWithError:&error];
 
if (!_products){
      NSAssert(FALSE, @"Error during select query execution:%@", error.description);
}

Data Synchronization with the Cloud

Now let’s synchronize the persisted data with Telerik Backend Services. To do that, you should initialize the context with a cloud client and synchronization policy as follows:

// the APIKey is unique per application

NSString* apiKey =  @"SaMplEKey"; 

// the access token is obtained from the Auth request's response

NSString* accessToken =  @"S@mp1eAcceSSToken";
TKEverliveClient*  everlive = [TKEverliveClient clientWithApiKey:apiKey accessToken:accessToken serviceVersion:@1];
 
TKReachabilityOptions options = TKSyncIn3GNetwork | TKSyncInWIFINetwork;
TKSyncPolicy* policy = [[TKSyncPolicy alloc] initForSyncOnDemandWithReachabilityOptions:options conflictResolutionType:TKPreferLocalInstance syncTimeout:100.0];
 
context = [[TKDataSyncContext alloc] initWithLocalStoreName:@"localDBName" cloudService:everlive syncPolicy:policy];

The next step is to create a mirror image of the Person table at the Telerik Backend Services portal as it is shown in the picture:

Telerik Backend Services Product Table Definition

The whole process of your backend data storage preparation is described in greater details here. Having the backend prepared, the only method that we should call if we want to trigger the synchronization is syncData:

NSError* error = nil;
[context syncData:&error];

During the synchronization, all the locally changed data will be merged with the remote changes according to the chosen policy. Of course, there is an option for a custom data merge that can better meet the specific business rules of an application. This is allowed via the implementation of the TKDataSyncDelegate protocol. For more information, you can check example project after you have installed the Telerik UI for iOS suite

Happy coding!

About the Author

Nikolay Diyanov

Diyanov is the Product Manager of the Native Mobile UI division at Progress. Delivering outstanding solutions that make developers' lives easier is his passion and the biggest reward in his work. In his spare time, Nikolay enjoys travelling around the world, hiking, sun-bathing and kite-surfing.

Find him on Twitter @n_diyanov or on LinkedIn.

Related Posts

Comments

Comments are disabled in preview mode.