Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Couchbase Mobile 101: How to Build Your First Mobile App with Couchbase Mobile: Couchbase Connect 2015

1,976 views

Published on

Jens will take you through the process of building an app from scratch that works online and offline. Learn about reading & writing Data, modeling app data the NoSQL way, adding sync in less than 5 minutes, enabling authentication, restricting data access, and adding role based security. By the end of this session you will be able to design and build your own app with Couchbase Mobile.

Published in: Technology
  • Be the first to comment

  • Be the first to like this

Couchbase Mobile 101: How to Build Your First Mobile App with Couchbase Mobile: Couchbase Connect 2015

  1. 1. COUCHBASE MOBILE 101 Jens Alfke Mobile Architect Couchbase, Inc.
  2. 2. ©2015 Couchbase Inc. A Whirlwind 45 Minutes Couchbase Mobile features Focus on Couchbase Lite: Installing and running a sample app A tour of Couchbase Lite’s architecture and API …as shown in the sample app’s code Next steps 2
  3. 3. ©2015 Couchbase Inc. What Is Couchbase Mobile? Couchbase Lite Client database library For mobile or desktop apps Couchbase Sync Gateway App server Front-end for Couchbase Server 3
  4. 4. ©2015 Couchbase Inc. What Makes Couchbase Mobile Awesome? Replication Transparent availability of data on server & client Full offline functionality Data orchestration Smart routing of data to its users Subsetting data sets for different use cases 4
  5. 5. Couchbase Sync Gateway …will be introduced in Couchbase Mobile 102 at 1:45pm
  6. 6. Couchbase Lite …will be introduced almost immediately
  7. 7. ©2015 Couchbase Inc. Couchbase Lite Embedded database library for mobile apps Flexible, schemaless data model (“NoSQL”) Querying by map/reduce views Active change notifications, for reactive UIs Full-strength multi-master replication 7
  8. 8. ©2015 Couchbase Inc. Couchbase Mobile Replication Avoid network latency for data access Offline support (if you want it) Control over conflict resolution Arbitrary topologies, including P2P Open protocol 8
  9. 9. Running Your First App
  10. 10. ©2015 Couchbase Inc. Running Your First iOS App Install Xcode 6 from Mac App Store Download Couchbase Lite Download Grocery Sync sample code Copy framework into sample app folder Build & Run the Xcode project 10
  11. 11. ©2015 Couchbase Inc. 2. Download Couchbase Lite couchbase.com/nosql-databases/downloads Click “Couchbase Mobile” 11
  12. 12. ©2015 Couchbase Inc. 3. Download Grocery Sync Source Code github.com/couchbaselabs/Grocery-Sync-iOS 12
  13. 13. ©2015 Couchbase Inc. 4. Plug In The Framework optio n 13
  14. 14. ©2015 Couchbase Inc. 5. Open The Project 14
  15. 15. ©2015 Couchbase Inc. Running Your First Android App Install Android Studio with Android SDK 19 & Build Tool 19 Check out Grocery Sync sample code repo Import Grocery Sync gradle file into Android Studio Press Debug or Run button 15
  16. 16. ©2015 Couchbase Inc. 2. Download Grocery Sync Source Code 16
  17. 17. ©2015 Couchbase Inc. 3. Import ‘build.gradle’ into Android Studio 17
  18. 18. Quick Demo
  19. 19. A Tour Of The Code And The API in large friendly letters
  20. 20. 1. Initialization & Insertion
  21. 21. ©2015 Couchbase Inc. Initialization DemoAppDelegate.m:61 // Initialize Couchbase Lite and find/create my database: NSError* error; self.database = [[CBLManager sharedInstance] databaseNamed: kDatabaseName error: &error]; if (!self.database) { [self showAlert: @"Couldn't open database" error: error fatal: YES]; return NO; } 21
  22. 22. ©2015 Couchbase Inc. Initialization MainActivity.java:118 manager = new Manager(new AndroidContext(this), Manager.DEFAULT_OPTIONS); database = manager.getDatabase(DATABASE_NAME); 22
  23. 23. ©2015 Couchbase Inc. Manager Top-level object, usually a singleton A collection of named databases Locates databases in filesystem 23
  24. 24. ©2015 Couchbase Inc. Databases And Documents Database “to-do” “doc1” “doc2” “doc3” Document “doc2” { “text”: “Vacuum kitchen”, “created”: “2015-05–28”, “check”: false, “tags”: [“jens”, “chore”] } thumb.jpg 24
  25. 25. ©2015 Couchbase Inc. Database Namespace for documents Contains views and their indexes Contains validation functions Source and target of replication 25
  26. 26. ©2015 Couchbase Inc. Document “_id” is unique within database, immutable JSON allows nested data structures (arrays, maps) Schemaless — Documents needn’t have the same structure Different types of docs can coexist in a database “type” property is a common convention Documents are versioned Optimistic concurrency control (MVCC) 26
  27. 27. ©2015 Couchbase Inc. Adding New Items RootViewController.m:243 // Create the new document's properties: NSDictionary *document = @{@"text": text, @"check": @NO, @"created_at": [CBLJSON JSONObjectWithDate: [NSDate date]]}; // Save the document: CBLDocument* doc = [database createDocument]; NSError* error; if (![doc putProperties: document error: &error]) { [self showErrorAlert: @"Couldn't save new item" forError: error]; } 27
  28. 28. ©2015 Couchbase Inc. Adding New Items MainActivity.java:343 SimpleDateFormat dateFormatter = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); UUID uuid = UUID.randomUUID(); Calendar calendar = GregorianCalendar.getInstance(); long currentTime = calendar.getTimeInMillis(); String currentTimeString = dateFormatter.format(calendar.getTime()); String id = currentTime + "-" + uuid.toString(); Document document = database.createDocument(); Map<String, Object> properties = new HashMap<String, Object>(); properties.put("_id", id); properties.put("text", text); properties.put("check", Boolean.FALSE); properties.put("created_at", currentTimeString); document.putProperties(properties); 28
  29. 29. 2. Views & Queries
  30. 30. ©2015 Couchbase Inc. Creating A Database View RootViewController.m:101 // Define a view with a map function that indexes to-do items by creation date: [[theDatabase viewNamed: @"byDate"] setMapBlock: MAPBLOCK({ id date = doc[@"created_at"]; if (date) emit(date, nil); }) version: @"1.1"]; 30
  31. 31. ©2015 Couchbase Inc. Map/Reduce Views Concept from functional programming, via Google App-defined map function operates on documents doc ⟼ { (key, value) …} Its output generates an index ordered by key Index rows can be aggregated by a reduce function Index is queried by key or key range 31
  32. 32. ©2015 Couchbase Inc. Map/Reduce Indexing emit(doc.created, doc.title) key value docID “2013-03-12” “taxes” “doc17” “2013-09-30” “call mom” “doc62” “2013-10-17” “cat food” “doc82” “2013-10-17” “tea bags” “doc83” “2013-10-22” “upgrade” “doc90” View “byDate” 32
  33. 33. ©2015 Couchbase Inc. Creating A Database View RootViewController.m:101 // Define a view with a map function that indexes to-do items by creation date: [[theDatabase viewNamed: @"byDate"] setMapBlock: MAPBLOCK({ id date = doc[@"created_at"]; if (date) emit(date, nil); }) version: @"1.1"]; 33
  34. 34. ©2015 Couchbase Inc. Creating A Database View MainActivity.java:122 com.couchbase.lite.View viewItemsByDate = database.getView(String.format("%s/%s", designDocName, byDateViewName)); viewItemsByDate.setMap(new Mapper() { @Override public void map(Map<String, Object> document, Emitter emitter) { Object createdAt = document.get("created_at"); if (createdAt != null) { emitter.emit(createdAt.toString(), null); } } }, "1.0"); 34
  35. 35. ©2015 Couchbase Inc. Driving the Table from a View Query RootViewController.m:101 // Create a query sorted by descending date, i.e. newest items first: CBLLiveQuery* query = [[[database viewNamed:@"byDate"] query] asLiveQuery]; query.descending = YES; // Plug the query into the CBLUITableSource, which uses it to drive the table. // (The CBLUITableSource uses KVO to observe the query's .rows property.) self.dataSource.query = query; self.dataSource.labelProperty = @"text"; @property(nonatomic, strong) IBOutlet UITableView *tableView; @property(nonatomic, strong) IBOutlet CBLUITableSource* dataSource; RootViewController.h:41 35
  36. 36. ©2015 Couchbase Inc. Driving the Table from a View Query RootViewController.m:101 liveQuery = view.createQuery().toLiveQuery(); liveQuery.addChangeListener(new LiveQuery.ChangeListener() { public void changed(final LiveQuery.ChangeEvent event) { runOnUiThread(new Runnable() { public void run() { grocerySyncArrayAdapter.clear(); for (Iterator<QueryRow> it = event.getRows(); it.hasNext();) { grocerySyncArrayAdapter.add(it.next()); } grocerySyncArrayAdapter.notifyDataSetChanged(); 36
  37. 37. ©2015 Couchbase Inc. LiveQuery Querying a View 37 key value docID “2013-03-12” “taxes” “doc17” “2013-09-30” “call mom” “doc62” “2013-10-17” “cat food” “doc82” “2013-10-17” “tea bags” “doc83” “2013-10-22” “upgrade” “doc90” } Query QueryRow QueryRow QueryRow .limit = 3 .offset = … .reverse = … .startKey = … .endKey = …
  38. 38. ©2015 Couchbase Inc. LiveQuery Driving An iOS UITableView 38 LiveQuery Query CBLUITable- Source UITableView Your Controller
  39. 39. ©2015 Couchbase Inc. LiveQuery Driving An iOS UITableView 39
  40. 40. ©2015 Couchbase Inc. Displaying Table Cells DemoAppDelegate.m:131 - (void)couchTableSource:(CBLUITableSource*)source willUseCell:(UITableViewCell*)cell forRow:(CBLQueryRow*)row { // Set the cell background and font: ……… // Configure the cell contents. Map function (above) copies the doc properties // into its value, so we can read them without having to load the document. NSDictionary* rowValue = row.value; BOOL checked = [rowValue[@"check"] boolValue]; if (checked) { cell.textLabel.textColor = [UIColor grayColor]; cell.imageView.image = [UIImage imageNamed:@"checked"]; } else { cell.textLabel.textColor = [UIColor blackColor]; cell.imageView.image = [UIImage imageNamed: @"unchecked"]; } // cell.textLabel.text is already set, thanks to setting up labelProperty } 40
  41. 41. ©2015 Couchbase Inc. Displaying Table Cells GrocerySyncArrayAdapter.java:33 public View getView(int position, View itemView, ViewGroup parent) { //... TextView label = ((ViewHolder)itemView.getTag()).label; QueryRow row = getItem(position); SavedRevision currentRevision = row.getDocument().getCurrentRevision(); // Check box Object check = (Object) currentRevision.getProperty("check"); boolean isGroceryItemChecked = false; if (check != null && check instanceof Boolean) isGroceryItemChecked = ((Boolean)check).booleanValue(); // Text String groceryItemText = (String) currentRevision.getProperty("text"); label.setText(groceryItemText); 41
  42. 42. ©2015 Couchbase Inc. Responding To Taps DemoAppDelegate.m:131 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // Ask CBLUITableSource for the corresponding query row, and get its document: CBLQueryRow *row = [self.dataSource rowAtIndex:indexPath.row]; CBLDocument *doc = row.document; // Toggle the document's 'checked' property: NSMutableDictionary *docContent = [doc.properties mutableCopy]; BOOL wasChecked = [docContent[@"check"] boolValue]; docContent[@"check"] = @(!wasChecked); // Save changes: NSError* error; if (![doc.currentRevision putProperties: docContent error: &error]) { [self showErrorAlert: @"Failed to update item" forError: error]; } } 42
  43. 43. ©2015 Couchbase Inc. Responding To Taps MainActivity.java:243 public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { QueryRow row = (QueryRow) adapterView.getItemAtPosition(position); Document document = row.getDocument(); Map<String, Object> newProperties = new HashMap<String, Object>(document.getProperties()); boolean checked = ((Boolean) newProperties.get("check")).booleanValue(); newProperties.put("check", !checked); try { document.putProperties(newProperties); grocerySyncArrayAdapter.notifyDataSetChanged(); } catch (Exception e) { // ... 43
  44. 44. ©2015 Couchbase Inc. Change Notifications Three types: DatabaseChanged: Any document updated DocumentChanged: A specific document updated LiveQuery: Change in query result set Enables reactive programming Controller Model (Database) GUI View User event Update doc DocumentChangedRedraw 44 Replication (pull)
  45. 45. 3. Replication
  46. 46. ©2015 Couchbase Inc. Replication 47 Replication (push) Replication (pull) https://exam.pl/db local_db
  47. 47. ©2015 Couchbase Inc. Replication Replication is asynchronous Use database notifications to detect changes A Replication can be one-shot or continuous One-shot runs only until it “catches up” Continuous keeps going till stopped or app quits Continuous handles online/offline transitions gracefully Replications can be filtered 48
  48. 48. ©2015 Couchbase Inc. Creating Replications DemoAppDelegate.m:74 _pull = [database createPullReplication: serverDbURL]; _push = [database createPushReplication: serverDbURL]; _pull.continuous = _push.continuous = YES; // Observe replication progress changes, in both directions: NSNotificationCenter* nctr = [NSNotificationCenter defaultCenter]; [nctr addObserver: self selector: @selector(replicationProgress:) name: kCBLReplicationChangeNotification object: _pull]; [nctr addObserver: self selector: @selector(replicationProgress:) name: kCBLReplicationChangeNotification object: _push]; [_push start]; [_pull start]; 49
  49. 49. ©2015 Couchbase Inc. Creating Replications MainActivity.java:150 Replication pullReplication = database.createPullReplication(syncUrl); pullReplication.setContinuous(true); Replication pushReplication = database.createPushReplication(syncUrl); pushReplication.setContinuous(true); pullReplication.start(); pushReplication.start(); pullReplication.addChangeListener(this); pushReplication.addChangeListener(this); 50
  50. 50. ©2015 Couchbase Inc. Monitoring Replications DemoAppDelegate.m:98 - (void) replicationProgress: (NSNotificationCenter*)n { if (_pull.status == kCBLReplicationActive || _push.status == kCBLReplicationActive) { // Sync is active -- aggregate the progress of both replications and compute a fraction: unsigned completed = _pull.completedChangesCount + _push.completedChangesCount; unsigned total = _pull.changesCount+ _push.changesCount; NSLog(@"SYNC progress: %u / %u", completed, total); // Update the progress bar, avoiding divide-by-zero exceptions: [self.rootViewController showSyncStatus: (completed / (float)MAX(total, 1u))]; } else { // Sync is idle -- hide the progress bar and show the config button: NSLog(@"SYNC idle"); [self.rootViewController hideSyncStatus]; } // Check for any change in error status and display new errors: NSError* error = _pull.lastError ? _pull.lastError : _push.lastError; if (error != _syncError) { _syncError = error; if (error) { [self showAlert: @"Error syncing" error: error fatal: NO]; } } 51
  51. 51. ©2015 Couchbase Inc. Monitoring Replications MainActivity.java:381 public void changed(Replication.ChangeEvent event) { Replication replication = event.getSource(); Log.d(TAG, "Replication : " + replication + " changed."); if (!replication.isRunning()) { String msg = String.format("Replicator %s not running", replication); Log.d(TAG, msg); } else { int processed = replication.getCompletedChangesCount(); int total = replication.getChangesCount(); String msg = String.format("Replicator processed %d / %d", processed, total); Log.d(TAG, msg); } if (event.getError() != null) { showError("Sync error", event.getError()); } } 52
  52. 52. Whew! (we’re done now)
  53. 53. Next Steps
  54. 54. ©2015 Couchbase Inc. Next Sessions 1:45–2:30 — #102: Sync Gateway 3:45–4:30 — #103: Peer-to-Peer Sync 5:15–6:00 — #104: Mobile Games with Unity 55
  55. 55. ©2015 Couchbase Inc. Next Steps Try connecting Grocery Sync to your own Sync Gateway RTFM! Look at other sample apps: ToDo Lite Couchbase Connect Conference app (.NET/Xamarin) Visit our mobile developer forums 56
  56. 56. The End — Any Questions?

×