Webinar - Developing with Couchbase Lite iOS

Preview:

DESCRIPTION

Learn how to develop with Couchbase Lite for iOS in this technical lecture led by Jens Alfke, lead Couchbase Lite iOS developer. Walkthrough developing your first iOS application using Native APIs, and hear about what is to come for Couchbase Lite iOS. In this webinar you will see: An overview of the Couchbase Lite iOS API A step-by-step demonstration on how to develop a iOS application with Couchbase Lite A quick demo of a iOS app An explanation of the future plans for Couchbase Lite iOS

Citation preview

Developing  With  Couchbase  Lite  on  iOS

Jens  Al9e jens@couchbase.com

10/29/13

“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

GeNng  Up  And  Running

• Download  Couchbase  Lite  

• Download  Grocery  Sync  sample  code  

• Copy  framework  into  sample  app  folder  

• Build  &  Run

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

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

Plug  In  The  Framework

option

Live  Demo

A  Tour  of  the  Code  and  the  API

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

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

Manager

• Collec;on  of  named  databases  

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

• Manages  database  storage  (local  directory)

Database

• Namespace  for  documents  

• Contains  views  and  their  indexes  

• Contains  valida;on  func;ons  

• Source  and  target  of  replica;on

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

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

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.

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}

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)

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.

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

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

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

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

Wiring  Up  The  Table  ViewRootViewController.xib

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  }  

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

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

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

OK,  But  Where’s  The  Sync?

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

Replica;on

Database  “db”

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

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

notifications

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

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

Beyond  Grocery  Sync

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”

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)

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);

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

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”

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”

{

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

Coming  Up  Next!

Tuesday,  November  5,  10:00AM  PST  

“Developing  With  Android”

Recommended