From the 101 session in the Couchbase LIVE New York mobile track, we went over on how to get started with integrating Couchbase Lite to your iOS and Android projects.  From the “Couchbase Mobile 101: How to Build Your First Mobile App” slides, we explored the APIs of Couchbase Mobile by walking over the Grocery Sync sample application which can be found on the Github repo for iOS and Android.  In this blog, we will recap at a high level the Couchbase Lite features and APIs that were presented at the Couchbase 101 session as well as some of the code found in the Grocery Sync sample.  To start, you would download Couchbase Lite Enterprise Edition for the platform you are developing on and follow either the iOS tutorial or Android tutorial to integrate Couchbase Lite to your mobile projects. 

After bringing in Couchbase Lite to your mobile projects, we would need to initialize Couchbase Lite and retrieve or create a database.  Below are some Couchbase Mobile concepts and requirements we need.

[1] Manager

The Manager is the top level class to reference in creating a namespace for databases.  Creating a database is simply referencing a string name like below:

iOS

Android

 

With that code in place, we are able to retrieve the documents containing JSON out of the database accordingly.  The database also serves as the source and target for replication.   The documents each have their respective unique name and unique ID.  Beyond that they have JSON as their properties, where the JSON object is a set of name properties where its values may be names or strings, numbers, arrays, dictionaries, and etc.

[2] Documents

The Document includes an immutable document ID within the database where the document's body takes the form of a JSON nested object of key-value pairs.  To allow for different types of documents to co-exist in a database, the convention that is commonly used is to include a property called 'type' which then has a String that defines the type of your documents.  This is a technique used to keep track of different document types if there are more than one type in a database and also help with indexing.  Documents also contain revisions for the purposes of tracking change histories and conflicts therefore it is key to how replication works. 

To insert documents the 'createDocument()' method will return an ID of a document that is in a form of a randomly generated UUID.  In the sample app for iOS, an 'NSDictionary' is created to correspond to an 'NSObject' in Objective-C with properties of 'text', 'check' and 'created_at' defined.  Below for ..   

iOS

we create the new document's properties and then save the document by referencing the datebase for the 'createDocument()' method.  The 'JSONObjectWithDate' is an utility function for taking a Cocao date object and converts into an ISO8601 String format as dates cannot be stored as native objects in JSON.

For Android, the 'SimpleDateFormat' class is creating the 'currentTimeString' for the object where the document ID is constructed by the combination of the 'currentTime' from the 'Calendar' class and the UUID from the 'randomUUID()' method.  Referencing the 'database' that was created from the Manager class, a document is created by calling the same 'createDocument()' method as in iOS.  In Android, Key-Value pairings resemble a HashMap object structure and so we create a Map that is the Java equivalent of the JSON object in the 'properties' variable.  The same three properties are inserted into the Java map by the 'put()' method and then to persist to disk, the Map object is passed into the 'putProperties()' method.  This is illustarated below:      

Android

 

[3] Attachments

The Attachment feature, though not used in the sample, allow documents to attach any arbitrarily size binary blob and thus is a technique on optimizing for replication where document updates are independent of attachment updates as they are stored separately from the JSON body.  For example, this may be an use-case for when the meta-data, document JSON, is changed on an associated attachment and therefore if a document is updated without changes to an attachment, then the replicator can skip sending the attachment.    

[4] Views

The View allows applications to create and maintain secondary indexes using the map & reduce technique.  We start off with a JSON document and the Map Function is the function you write that takes that document as input and outputs a set of key-value pairs.  The output of that map function that runs across all documents in the database generates an index.  In the Grocery Sync sample applicaiton, we are defining a View with a map function that indexes the to-do items by creation date.  Creating a View for.. 

iOS

 

For iOS, first we are creating a ‘View’ on the database.  The database is also a container or namespace for the ‘Views’ , so we say 'viewNamed: @“byDate”' where if the View does not exist already we will then create it and if it does, we will return it.  The rest of the block of code is to set its Map Block.  So this is a MapReduce View and so it means it has a map function.  We get the date out of the document by looking at the 'created_at' property.  And if the document has one, we emit that as the key. In the Grocery Sync app, it is not emitting anything for the value because it is actually going back to grab the document itself to get the rest of the data from.  The version string, '@”1.1″' at the end is used to communicate with the database on whether or not your map function has changed.  Since the database can not tell when the map function has changed from one run to the next; a technique is to increase the version string to tell the database to throw away the current index and rebuild it.  

Android

This is the Java-Android version where similar to iOS, we call, 'database.getView()' and then create the 'map function' using some inner class syntax which in Java exists as a 'Mapper()' object.  The indexes are able to be updated on demand and useful information are able to be extracted from the document that you want to index where then the key-value can be emitted.  Everytime where something changes, the map function gets fed that document. The map function calls a function called 'emit()' which takes the ‘key and value’ as parameters. 

What the Grocery Sync application is doing here is generating the index of all the to-do item that is shorted by 'key' and since the key is the time-stamp, the items will be chronologically ordered where the value strings are the names of the 'to-do' items.  The index also remembers the document ID of the document that emitted that key-value pair.  So when you are querying and when you have a row in your query, you may use that to go back to the document, and fetch that entire row out of the database if you want to.  The idea here is once you have this index, you would query it. The query mindset is done by saying either, “I want all the index entries with a particular key, or a set of keys, or a range of keys.”

[5] Queries

Queries can then look up a range of rows from a view, and either use the rows' keys and values directly or get the documents they came from from the document ID.  In the code below, we are driving the table from a View Query by creating a query that is sorted by descending date where the newest items are displayed first. 

iOS

With the items in the index, we want to use that index to drive table view which is the main Grocery Sync UI.  In iOS, we generate a query that is 'viewNamed: @”byDate”' and call query which creates query on it.  Then we see ‘LiveQuery’ where it is a special subset of query that will actually track the View over time.  We set the 'descending' property on the query to ‘yes’ as we want to get the rows in descending order of the dates in to have the newest created items on top.  Lastly with the iOS specific code, we are telling the 'dataSource' about the query and indicating which property to show as the label in table view. 

Android

 

Similarly, the Java-Android version starts up the same way where a query is created by having the view call 'toLiveQuery()' on it to generate the 'liveQuery'.  And then 'addChangeListener()' is executed on that liveQuery, followed by the 'changed()' method being called for the Query updates, which is whenever the result of that Query changes.  And the output for when you run the query is an array of ‘QueryRows’ where each QueryRow is an object and has properties like Key and Value and DocumentID but and also a document property which will load the document back out of the database.  It is going through an 'Iterator()' to get all the rows out of the query and add them to the 'grocerySyncArrayAdapter' which is a custom class it has for storing the dataset. 
[6] LiveQuery

We can think of LiveQuery as a wrapper around the query which listens for change notifications from the database.  So when the database changes, the LiveQuery will kick off the Query again, re-running it in the background asynchronously.  And then compare the query results with the previous results that it already had.  If the results have changed, the LiveQuery will signal its own notification events which the application may then handle accordingly like redrawing the UI based on that new Query.  Below, the code illustrates how to display the Table Cells for..

iOS

For the sample application here, the 'CBLUITableSource' from Couchbase Lite will act as an intermediary between the LiveQuery and the UITableView, receiving change notifications and thus driving the table based on a Query.  It also acts the data source object for the tableView, which means that it is the object that the tableView is going to ask to provide all the data for the rows.  Your 'controller’ object then becomes the delegate of the UITableView where it will get notifications from the TableView about when the user taps on one of the rows. 

Android

Here is the Android equivalent in the Grocery Sync Adapter class.  It is getting the 'QueryRow' by calling ‘getItem()’ based on the row number in the table.  It then gets the document from the Query Row and gets its current revision. Then it uses the check and text properties to populate the UI Controls in the row. 

Lastly for the Grocery Sync sample application, we need to actually respond to a tap on a row on the table such as toggling the check mark.  Below we illustrate how this is done by referencing the QueryRow at a particular index and retrieve the document out of it.

iOS

This is a method call upon the UITableView itself on its delegate.  Telling that a row got selected which means ‘TAPPED’ So it is going to go to the data source. Which is the UI Table Source object, and ask it for the Query Row at that index, and get the document out of it. So now it is basically doing a Read, Write, Modify cycle on that document. Where it gets the properties..makes a mutable copy of the properties where we now have a mutable dictionary where we can update.  Reads the checked property out as a Boolean, and writes it back in as the opposite. So this is inverting the Boolean value of the checked property. Then it calls putProperties in the end to save that value back in. 

Android

Moving to the Android version now, we have an 'onItemClick()' that gets called by the Android GUI.  It is going to get its QueryRow at that position, get the document, get the properties out and then put the properties.  In the Java APIs it is idiomatic to use exceptions where it is not in objective C so this has a try-catch wrapped around the handle in saving the document.   If in the Time Window something else modified the document, probably the replicator, then this would throw an error. You would get a conflict error.

 

Next we will go into the Couchbase Lite Replicator class and the Couchbase Mobile Developer Portal is a great resource to start from.

 From there we will dive into Couchbase Sync Gateway in our 102 session, where I will talk about “How to Add Secure Sync to your Mobile application.”  We will round out the day with how to enable the Peer-to-Peer feature of Couchbase Mobile in the 103 session with Austin Gonyou, where you can create unique social in-app experiences by “Building a Peer-to-Peer App with Couchbase Mobile.”

Posted by William Hoang, Mobile Developer Advocate, Couchbase

William was a Developer Advocate on the Mobile Engineering/Developer Experience team at Couchbase. His love for coffee and code has transcended him into the world of mobile while appreciating the offline in-person experiences. Prior, William worked on the Developer Relations team over at Twitter, BlackBerry, and Microsoft while also having been a Software Embedded GPS engineer at Research In Motion. William graduated from McGill University in Electrical Software Engineering

Leave a reply