Transcript
Page 1: Webinar - Developing with Couchbase Lite iOS

Developing  With  Couchbase  Lite  on  iOS

Jens  Al9e [email protected]

10/29/13

Page 2: Webinar - Developing with Couchbase Lite 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 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 iOS

1.  Download  Couchbase  Litewww.couchbase.com/communi:es/couchbase-­‐lite

Page 5: Webinar - Developing with Couchbase Lite iOS

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

Page 6: Webinar - Developing with Couchbase Lite iOS

Plug  In  The  Framework

option

Page 7: Webinar - Developing with Couchbase Lite iOS

Live  Demo

Page 8: Webinar - Developing with Couchbase Lite iOS

A  Tour  of  the  Code  and  the  API

Page 9: Webinar - Developing with Couchbase Lite iOS

Ini;aliza;onDemoAppDelegate.m:64

       //  Initialize  Couchbase  Lite  and  find/create  my  database:          NSError*  error;          self.database  =  [[CBLManager  sharedInstance]  createDatabaseNamed:  kDatabaseName                                                                                                                                error:  &error];          if  (!self.database)                  [self  showAlert:  @"Couldn't  open  database"  error:  error  fatal:  YES];  

Page 10: Webinar - Developing with Couchbase Lite 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 11: Webinar - Developing with Couchbase Lite iOS

Manager

• Collec;on  of  named  databases  

• Generally  a  singleton  Unless  you  run  on  mul:ple  threads  

• Manages  database  storage  (local  directory)

Page 12: Webinar - Developing with Couchbase Lite iOS

Database

• Namespace  for  documents  

• Contains  views  and  their  indexes  

• Contains  valida;on  func;ons  

• Source  and  target  of  replica;on

Page 13: Webinar - Developing with Couchbase Lite 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  

Denormaliza:on  is  OK  —  use  arrays  or  dic:onaries  

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

• Versioned  Mul:-­‐Version  Concurrency  Control  (MVCC)  

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

Revision  ID  history  is  stored

Page 14: Webinar - Developing with Couchbase Lite iOS

Ini;aliza;onDemoAppDelegate.m:64

       //  Initialize  Couchbase  Lite  and  find/create  my  database:          NSError*  error;          self.database  =  [[CBLManager  sharedInstance]  createDatabaseNamed:  kDatabaseName                                                                                                                                error:  &error];          if  (!self.database)                  [self  showAlert:  @"Couldn't  open  database"  error:  error  fatal:  YES];  

Page 15: Webinar - Developing with Couchbase Lite iOS

Crea;ng  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 16: Webinar - Developing with Couchbase Lite 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 17: Webinar - Developing with Couchbase Lite iOS

Views

• Map/Reduce  mechanism  Popular  in  other  NoSQL  databases  

A  view  is  similar  to  index  in  rela:onal  database.  

• App-­‐defined  map  func;on  Called  on  every  document  

Can  emit  arbitrary  key/value  pairs  into  the  index  

• Op;onal  reduce  func;on  Data  aggrega:on  /  grouping  

• Func;ons  are  registered  as  na;ve  blocks/callbacks  Not  stored  as  JavaScript  in  “design  document”  (as  in  CouchDB)

Page 18: Webinar - Developing with Couchbase Lite iOS

Crea;ng  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 19: Webinar - Developing with Couchbase Lite iOS

Driving  the  Table  from  a  View  QueryRootViewController.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  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 20: Webinar - Developing with Couchbase Lite iOS

Queries

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

No  joins  or  fancy  sor:ng  

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

• LiveQuery  subclass  Monitors  database,  pushes  no:fica:ons  

Uses  KVO  on  iOS,  can  drive  a  UITableView  

• Upcoming  goodies!  Full-­‐text  indexing  

Geo  (bounding-­‐box)  queries

Page 21: Webinar - Developing with Couchbase Lite 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 22: Webinar - Developing with Couchbase Lite iOS

Driving  the  Table  from  a  View  QueryRootViewController.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  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 23: Webinar - Developing with Couchbase Lite iOS

Wiring  Up  The  Table  ViewRootViewController.xib

Page 24: Webinar - Developing with Couchbase Lite iOS

Displaying  Table  CellsDemoAppDelegate.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 25: Webinar - Developing with Couchbase Lite iOS

Responding  To  TapsDemoAppDelegate.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];          }  }  

Page 26: Webinar - Developing with Couchbase Lite iOS

Adding  New  ItemsDemoAppDelegate.m:247

-­‐(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  untitledDocument];          NSError*  error;          if  (![doc  putProperties:  inDocument  error:  &error])  {                  [self  showErrorAlert:  @"Couldn't  save  new  item"  forError:  error];            

Page 27: Webinar - Developing with Couchbase Lite iOS

Dele;ng  ItemsDemoAppDelegate.m:191

-­‐  (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 28: Webinar - Developing with Couchbase Lite iOS

OK,  But  Where’s  The  Sync?

Page 29: Webinar - Developing with Couchbase Lite iOS

Crea;ng  Replica;onsDemoAppDelegate.m:291

       //  Tell  the  database  to  use  this  URL  for  bidirectional  sync.          //  This  call  returns  an  array  of  the  pull  and  push  replication  objects:          NSArray*  repls  =  [self.database  replicateWithURL:  newRemoteURL  exclusively:  YES];          if  (repls)  {                  _pull  =  repls[0];                  _push  =  repls[1];                  _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];          }  

Page 30: Webinar - Developing with Couchbase Lite iOS

Replica;on

Database  “db”

Replica;onDir:   push  Remote:  hkp://server/db  Auth:   <token>

Replica;onDir:   pull  Remote:  hkp://server/db  Auth:   <token>

notifications

Page 31: Webinar - Developing with Couchbase Lite iOS

Replica;on

• Each  Replica;on  is  one-­‐direc;onal  (push  or  pull)  

• Replica;ons  can  be  one-­‐shot,  con;nuous  or  persistent  One-­‐shot:  Stops  when  complete.  

Con:nuous:  Keeps  monitoring  changes  :ll  app  quits  

Persistent:  Automa:cally  starts  again  on  next  relaunch  

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

You  just  see  document-­‐changed  or  query-­‐changed  no:fica:ons.  

• Progress  is  observable  through  KVO  or  NSNo;fica;on

Page 32: Webinar - Developing with Couchbase Lite iOS

Monitoring  Replica;onsDemoAppDelegate.m:353

//  Called  in  response  to  replication-­‐change  notifications.  Updates  the  progress  UI.  -­‐  (void)  replicationProgress:  (NSNotificationCenter*)n  {          if  (_pull.mode==  kCBLReplicationActive  ||  _push.mode==  kCBLReplicationActive)  {                  //  Sync  is  active  -­‐-­‐  aggregate  progress  of  both  replications:                  unsigned  completed  =  _pull.completed  +  _push.completed;                  unsigned  total  =  _pull.total  +  _push.total;                  [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.error  ?  _pull.error  :  _push.error;          if  (error  !=  _syncError)  {                  _syncError  =  error;                  if  (error)                          [self  showErrorAlert:  @"Error  syncing"  forError:  error];  

Page 33: Webinar - Developing with Couchbase Lite iOS

Beyond  Grocery  Sync

Page 34: Webinar - Developing with Couchbase Lite 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 35: Webinar - Developing with Couchbase Lite iOS

Models

• Kind  of  like  NSManagedObject,  but  simpler  

• Map  JSON  to  na;ve  @proper;es  Scalar  types  (int,  bool…),  String,  Date,  Data  (blob)  

References  to  other  doc  models  

Arrays  of  the  above  

• Proper;es  are  KV-­‐observable  

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

• No  query-­‐based  rela;on  support  (yet)

Page 36: Webinar - Developing with Couchbase Lite iOS

Represen;ng  Document  Types

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

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

Page 37: Webinar - Developing with Couchbase Lite 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 38: Webinar - Developing with Couchbase Lite iOS

Querying  With  Mul;ple  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 39: Webinar - Developing with Couchbase Lite iOS

Querying  With  Mul;ple  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 40: Webinar - Developing with Couchbase Lite iOS

Whew!

hkp://couchbase.github.io/couchbase-­‐lite-­‐ios/docs/html/annotated.html

hkp://docs.couchbase.com/couchbase-­‐lite/cbl-­‐ios/

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

Page 41: Webinar - Developing with Couchbase Lite iOS

Coming  Up  Next!

Tuesday,  November  5,  10:00AM  PST  

“Developing  With  Android”


Recommended