webinar - developing with couchbase lite and ios

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

Upload: couchbase

Post on 28-Jun-2015

747 views

Category:

Technology


4 download

DESCRIPTION

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

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