Top Banner
Developing With Couchbase Lite on iOS 6/10/14 Jens Al9e [email protected]
45

Webinar - Developing with Couchbase Lite and iOS

Jun 28, 2015

Download

Technology

Couchbase

Learn how to develop with Couchbase Lite for iOS in this technical lecture led by Jens Alfke, lead Couchbase Lite iOS developer. The session will include a look into the Objective-C native APIs, using a walkthrough of a demo app.

Other topics include:

A 5-minute recap of the Couchbase Lite architecture
A step-by-step demonstration on how to develop an iOS application with Couchbase Lite
An explanation of the future plans for Couchbase Lite iOS
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Webinar - Developing with Couchbase Lite and iOS

Developing  With  Couchbase  Lite  on  iOS

6/10/14

Jens  Al9e [email protected]

Page 2: Webinar - Developing with Couchbase Lite and iOS

“Tell  Them  What  You’re  Going  To  Tell  Them”

• InstallaFon  and  setup  walk-­‐through  

• Demo  of  Grocery  Sync  sample  app  

• Tour  of  Grocery  Sync  code  with  digressions  about  the  API  

and  the  document  model  

and  querying  

• Beyond  Grocery  Sync

Page 3: Webinar - Developing with Couchbase Lite and iOS

GeNng  Up  And  Running

• Download  Couchbase  Lite  

• Download  Grocery  Sync  sample  code  

• Copy  framework  into  sample  app  folder  

• Build  &  Run

Page 4: Webinar - Developing with Couchbase Lite and iOS
Page 5: Webinar - Developing with Couchbase Lite and iOS

1.  Download  Couchbase  Litewww.couchbase.com/download#cb-­‐mobile

Page 6: Webinar - Developing with Couchbase Lite and iOS

Download  Grocery  Syncgithub.com/couchbaselabs/Grocery-­‐Sync-­‐iOS

Page 7: Webinar - Developing with Couchbase Lite and iOS

Plug  In  The  Framework

option

Page 8: Webinar - Developing with Couchbase Lite and iOS

Run  &  Sync

Page 9: Webinar - Developing with Couchbase Lite and iOS

Live  Demo

Page 10: Webinar - Developing with Couchbase Lite and iOS

A  Tour  of  the  Code  and  the  API

Page 11: Webinar - Developing with Couchbase Lite and iOS

IniFalizaFonDemoAppDelegate.m:64

       //  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];  

Page 12: Webinar - Developing with Couchbase Lite and iOS

CBLManager

Database  “otherdb”

CBLDatabase  “db”

CBLDocument  “doc3”

CBLDocument  “doc2”

Document  “doc1”

CBLDocument  “doc1”  !{ “text”: “Vacuum”, “created”: “2013-10-08”, “check”: false }

thumb.jpg

Manager,  Databases,  Documents

Page 13: Webinar - Developing with Couchbase Lite and iOS

Manager

• CollecFon  of  named  databases  

• Generally  a  singleton  Unless  you  run  on  mulEple  threads  

• Manages  database  storage  (local  directory)

Page 14: Webinar - Developing with Couchbase Lite and iOS

Database

• Namespace  for  documents  

• Contains  views  and  their  indexes  

• Contains  validaFon  funcFons  

• Source  and  target  of  replicaFon

Page 15: Webinar - Developing with Couchbase Lite and iOS

Document

• Has  unique  ID  within  its  database  

• Contains  arbitrary*  JSON  object  *except  keys  that  start  with  “_”  are  reserved  

There  is  no  explicit,  enforced  schema  

DenormalizaEon  is  OK  —  use  arrays  or  dicEonaries  

• May  contain  binary  aaachments  Data  blobs,  can  be  large,  tagged  with  MIME  type  

• Versioned  MulE-­‐Version  Concurrency  Control  (MVCC)  

Every  update  creates  a  revision  ID  (based  on  digest  of  contents)  

Revision  ID  history  is  stored

Page 16: Webinar - Developing with Couchbase Lite and iOS

IniFalizaFonDemoAppDelegate.m:64

       //  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];  

Page 17: Webinar - Developing with Couchbase Lite and iOS

CreaFng  a  Database  ViewRootViewController.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,  doc);          })  reduceBlock:  nil  version:  @"1.1"];  

Not a UIView — a database “view” is like an index.

Page 18: Webinar - Developing with Couchbase Lite and iOS

View  “completed”

Views  &  Queries

CBLDatabase  “db”

CBLView  “byDate”

function(doc) { emit(doc.created, doc.title); }map function

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 index

CBLQuery}

Page 19: Webinar - Developing with Couchbase Lite and iOS

Views

• Map/Reduce  mechanism  Popular  in  other  NoSQL  databases  

A  view  is  similar  to  index  in  relaEonal  database  

• App-­‐defined  map  funcFon  Called  on  every  document  

Can  emit  arbitrary  key/value  pairs  into  the  index  

• OpFonal  reduce  funcFon  Data  aggregaEon  /  grouping  

• FuncFons  are  registered  as  naFve  blocks/callbacks  Not  stored  as  JavaScript  in  “design  document”  (as  in  CouchDB)

Page 20: Webinar - Developing with Couchbase Lite and iOS

CreaFng  a  Database  ViewRootViewController.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,  doc);          })  reduceBlock:  nil  version:  @"1.1"];  

Not a UIView — a database “view” is like an index.

Page 21: Webinar - Developing with Couchbase Lite and iOS

Driving  the  Table  from  a  View  QueryRootViewController.m:69

       //  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  will  use  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

Page 22: Webinar - Developing with Couchbase Lite and iOS

Queries

• Basic  feature  set  Key  ranges,  offset/limit,  reverse,  group  by  key…  

No  joins  or  fancy  sorEng  

but  compound  keys  (and  clever  emits)  allow  for  some  tricks  

• LiveQuery  subclass  Monitors  database,  pushes  noEficaEons  

Uses  KVO  on  iOS,  can  drive  a  UITableView  

• iOS  goodies!  Full-­‐text  indexing  

Geo  (bounding-­‐box)  queries

Page 23: Webinar - Developing with Couchbase Lite and iOS

Live  Queries  &  Table  Data  Sources

key value docID

“2013-­‐09-­‐30” “Pencil  shavings”“doc62”

“2013-­‐10-­‐17” “Mangos” “doc82”

“2013-­‐10-­‐17” “second” “doc83”

view index CBLLiveQuery

CBLQuery}data source

CBLUI-­‐TableSource

Page 24: Webinar - Developing with Couchbase Lite and iOS

Driving  the  Table  from  a  View  QueryRootViewController.m:69

       //  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  will  use  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

Page 25: Webinar - Developing with Couchbase Lite and iOS

Combining  CreaFng+Querying

// Returns a query for all the lists in a database. + (CBLQuery*) queryListsInDatabase: (CBLDatabase*)db { CBLView* view = [db viewNamed: @"lists"]; if (!view.mapBlock) { // Register the map function, the first time we access the view: [view setMapBlock: MAPBLOCK({ if ([doc[@"type"] isEqualToString:kListDocType]) emit(doc[@"title"], nil); }) reduceBlock: nil version: @"1"]; // bump version any time you change the MAPBLOCK body! } return [view createQuery]; }

Page 26: Webinar - Developing with Couchbase Lite and iOS

Wiring  Up  The  Table  ViewRootViewController.xib

Page 27: Webinar - Developing with Couchbase Lite and iOS

Displaying  Table  CellsRootViewController.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  }  

Page 28: Webinar - Developing with Couchbase Lite and iOS

Responding  To  TapsRootViewController.m:167

-­‐  (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];          }  }  

Page 29: Webinar - Developing with Couchbase Lite and iOS

Adding  New  ItemsRootViewController.m:248

-­‐(void)textFieldDidEndEditing:(UITextField  *)textField  {          //  Get  the  name  of  the  item  from  the  text  field:     NSString  *text  =  addItemTextField.text;                if  (text.length  ==  0)  {                  return;          }          addItemTextField.text  =  nil;  !        //  Create  the  new  document's  properties:     NSDictionary  *inDocument  =  @{                        @"text":  text,                  @"check":  @NO,                  @"created_at":  [CBLJSON  JSONObjectWithDate:  [NSDate  date]]          };                    //  Save  the  document:          CBLDocument*  doc  =  [database  createDocument];          NSError*  error;          if  (![doc  putProperties:  inDocument  error:  &error])  {                  [self  showErrorAlert:  @"Couldn't  save  new  item"  forError:  error];            

Page 30: Webinar - Developing with Couchbase Lite and iOS

DeleFng  ItemsRootViewController.m:189

-­‐  (NSArray*)checkedDocuments  {          NSMutableArray*  checked  =  [NSMutableArray  array];          for  (CBLQueryRow*  row  in  self.dataSource.rows)  {                  CBLDocument*  doc  =  row.document;                  if  ([doc[@"check"]  boolValue])                          [checked  addObject:  doc];          }          return  checked;  }  !!-­‐  (void)deleteCheckedDocuments  {          NSError*  error;          if  (![dataSource  deleteDocuments:  self.checkedDocuments  error:  &error])  {                  [self  showErrorAlert:  @"Failed  to  delete  items"  forError:  error];          }  }

Page 31: Webinar - Developing with Couchbase Lite and iOS

OK,  But  Where’s  The  Sync?

Page 32: Webinar - Developing with Couchbase Lite and iOS

CreaFng  ReplicaFonsRootViewController.m:293

       _pull  =  [self.database  createPullReplication:  newRemoteURL];          _push  =  [self.database  createPushReplication:  newRemoteURL];          _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];  

Page 33: Webinar - Developing with Couchbase Lite and iOS

ReplicaFon

Database  “db”

ReplicaFonDir:   push  Remote:  hlp://server/db  Auth:   <token>

ReplicaFonDir:   pull  Remote:  hlp://server/db  Auth:   <token>

notifications

Page 34: Webinar - Developing with Couchbase Lite and iOS

ReplicaFon

• Each  ReplicaFon  is  one-­‐direcFonal  (push  or  pull)  

• ReplicaFons  can  be  one-­‐shot  or  conFnuous  One-­‐shot:  Stops  when  complete.  

ConEnuous:  Keeps  monitoring  changes  Ell  app  quits  

• Replicator  runs  in  a  background  thread  It  detects  online/offline,  handles  connecEon  errors,  retries…  

You  just  see  document-­‐changed  or  query-­‐changed  noEficaEons.  

• Progress  is  observable  through  KVO  or  NSNoFficaFon

Page 35: Webinar - Developing with Couchbase Lite and iOS

Monitoring  ReplicaFonsRootViewController.m:355

//  Called  in  response  to  replication-­‐change  notifications.  Updates  the  progress  UI.  -­‐  (void)  replicationProgress:  (NSNotificationCenter*)n  {          if  (_pull.status==kCBLReplicationActive  ||  _push.status==kCBLReplicationActive)          {                  //  Sync  is  active  -­‐-­‐  aggregate  progress  of  both  replications:                  unsigned  completed  =  _pull.completedChangesCount  +  _push.completedChangesCount;                  unsigned  total  =  _pull.changesCount  +  _push.changesCount;                  [self  showSyncStatus];                  //  Update  the  progress  bar,  avoiding  divide-­‐by-­‐zero  exceptions:                  progress.progress  =  (completed  /  (float)MAX(total,  1u));          }  else  {                  //  Sync  is  idle  -­‐-­‐  hide  the  progress  bar  and  show  the  config  button:                  [self  showSyncButton];          }  !        //  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  showErrorAlert:  @"Error  syncing"  forError:  error];  

Page 36: Webinar - Developing with Couchbase Lite and iOS

Beyond  Grocery  Sync

Page 37: Webinar - Developing with Couchbase Lite and iOS

Models

Task

List

Task

Task

@interface Task : CBLModel !@property NSString*title; @property NSDate* created; @property bool checked; @property List* list; !@end

@interface List : CBLModel !@property NSString* title; @property NSArray* members; !@end

Document  “doc23”

Document  “doc82”

Document  “doc99”

Document  “doc3”

Page 38: Webinar - Developing with Couchbase Lite and iOS

Models

• Kind  of  like  NSManagedObject,  but  simpler  

• Map  JSON  to  naFve  @properFes  Scalar  types  (int,  bool…),  String,  Date,  Data  (blob)  

References  to  other  doc  models  

Arrays  of  the  above  

• ProperFes  are  KV-­‐observable  

• Models  provide  mutable  state  -­‐save:  writes  to  underlying  document  

• No  query-­‐based  relaFon  support  (yet)

Page 39: Webinar - Developing with Couchbase Lite and iOS

RepresenFng  Document  Types

• There  are  no  tables  to  separate  different  record  types!  ConvenEon  is  to  use  a  “type”  property  

• Map  funcFons  can  pick  out  docs  with  the  right  type  if  (doc.type  ==  “item”)  emit(doc.created,  doc.text);

Page 40: Webinar - Developing with Couchbase Lite and iOS

To-­‐Do  List  With  ModelsFrom  ToDoLite  project

       Task*  task  =  [Task  modelForDocument:  row.document];          cell.textLabel.text  =  task.text;          cell.textLabel.textColor  =  task.checked  ?  [UIColor  grayColor]                                                                                          :  [UIColor  blackColor];  

@interface Task : CBLModel !@property NSString*title; @property NSDate* created; @property bool checked; @property List* list; !@end

Page 41: Webinar - Developing with Couchbase Lite and iOS

Querying  With  MulFple  ListsFrom  ToDoLite  project

       [view  setMapBlock:  MAPBLOCK({                  if  ([doc[@"type"]  isEqualToString:  kTaskDocType])  {                          id  date  =  doc[@"created_at"];                          NSString*  listID  =  doc[@"list_id"];                          emit(@[listID,  date],  doc);                  }          })  reduceBlock:  nil  version:  @"4"];

key docID

[“list1”,  “2013-­‐09-­‐30”] “doc82”

[“list2”,  “2013-­‐06-­‐02”] “doc62”

[“list2”,  “2013-­‐10-­‐17”] “doc83”

[“list2”,  “2013-­‐10-­‐28”] “doc90”

[“list3”,  “2013-­‐01-­‐01”] “doc01”

Page 42: Webinar - Developing with Couchbase Lite and iOS

Querying  With  MulFple  ListsFrom  ToDoLite  project

       CBLQuery*  query  =  [view  query];          query.descending  =  YES;          NSString*  myListId  =  self.document.documentID;          query.startKey  =  @[myListId,  @{}];          query.endKey  =  @[myListId];  

key docID

[“list1”,  “2013-­‐09-­‐30”] “doc82”

[“list2”,  “2013-­‐06-­‐02”] “doc62”

[“list2”,  “2013-­‐10-­‐17”] “doc83”

[“list2”,  “2013-­‐10-­‐28”] “doc90”

[“list3”,  “2013-­‐01-­‐01”] “doc01”

{

Page 43: Webinar - Developing with Couchbase Lite and iOS

Whew!

hlp://developer.couchbase.com/mobile/develop/guides/couchbase-­‐lite/index.html

hap://github.com/couchbaselabs/Grocery-­‐Sync-­‐iOS

haps://github.com/couchbaselabs/ToDoLite-­‐iOS

Page 44: Webinar - Developing with Couchbase Lite and iOS
Page 45: Webinar - Developing with Couchbase Lite and iOS

Q  &  A