41
Developing With Couchbase Lite on iOS Jens Al9e [email protected] 10/29/13

Webinar - Developing with Couchbase Lite iOS

Embed Size (px)

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

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”