Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Building Powerful Mac + iOS Apps with Couchbase Lite and Feather: Couchbase Connect 2015


Published on

Couchbase Lite is an incredibly powerful toolkit for building document-database-backed, offline-first, mobile apps. In this talk Matias will demonstrate how the same principles that make Couchbase Lite so great for mobile apps can be applied to develop complex desktop applications. He will also announce an open source library – Feather – which assists in object document mapping and domain modeling for a large Couchbase Lite-based application, which I has used over the past three years to create a full-blown offline-capable, collaborative word processor for complex scholarly documents.

Published in: Technology
  • Be the first to comment

Building Powerful Mac + iOS Apps with Couchbase Lite and Feather: Couchbase Connect 2015

  1. 1. Build offline first with Couchbase & Feather Matias Piipari (@mz2) CEO & Co-founder, CTO,
  2. 2. A Couchbase Lite powered writing tool for complex documents.
  3. 3. Word processor & Couchbase?
  4. 4. http://your-data
  5. 5. Web frontend Manuscript contents Author name / affiliation data Citation data Feather (Couchbase Lite model) Web model (e.g. PouchDB) Mac app UI
  6. 6. Sync and share your manuscripts
  7. 7. You own and control your data End-user is our customer, not part of the product.
  8. 8. Individual Individual + 
 co-author / supervisor Group of collaborators
  9. 9. 50+ model entity types
  10. 10. ~30K documents + attachments. Compresses to 60+ MB of pre-bundled content
  11. 11. Crowdsourced data
  12. 12. Cross-platform data access
  13. 13. Why Couchbase Lite? 1. Serve the data and its change feed over HTTP. 2. Focus on delivering a great offline experience. 3. Object-document mapping and attachments. 4. Sync is about conflict resolution and model robustness. 5. Sync is available peer-to-peer, between local databases. 6. Gives us + the end user control over data. 7. Cross-platform (Core Data). 8. Low memory footprint, fast start-up doesn't hurt on desktop. 9. Runtime performance drawbacks less of a concern on desktop. 10. Source code is readable.
  14. 14. ❤ Couchbase is a great open source project. ❤
  15. 15. "Why does everything have to work so differently in Couchbase?" — Anonymous Cocoa developer.
  16. 16. What is different? • Data modeling. • Database querying. • Consistency. • Data validation. • Performance bottlenecks. • [...]
  17. 17. Speed up common tasks. Impose structure and conventions.
  18. 18. Feather: utilities and conventions. • Partition data to multiple databases (speed + storage size). • Manage repositories, or 'collections', of model objects. • Mix-ins for unrelated model entities to share behaviour. • Pre-compute data shipped in the app. • Support nested object collections / trees in a document. • Help with making the data model scriptable.
  19. 19. Contents database Authors database Bibliography database Sections repository User visible database package A (~/Documents/thesis.manuscript) Paragraphs repository Styles repository Model object controllers Section model Paragraph model Paragraph style Model object Margin Citation repository Citation Cite “Piipari et al (2014)” Cite “Hubbard et al (2011)” Embedded objects Shared database package (~/Library/Application Support/Manuscripts) Styles User visible database package A (~/Documents/conf-abstract.manuscript) Contents database Authors database Bibliography database Styles Styles databaseTemplates databaseFunders database
  20. 20. ✨ Model objects and model object controllers ✨ • Model is dumb. • Object controller for CRUD. • XsController is implictly repository for models of type X (can be overriden). • Observer callbacks for didAddX, didUpdateX, didDeleteX given. • Properties which are prefixed cached are… cached. • Properties which are prefixed embedded contain… embedded objects. • Principle: if you're going to fail, detect it during startup.
  21. 21. Embedded objects • Nested in the containing object's JSON representation. • Nested collections supported (arrays, sets, dictionaries). • A nested object itself nests objects or collections of objects. • Propagates changes to containing top-level model object.
  22. 22. Bibliography database Database package Citation repository Citation Cite “Piipari et al (2014)” Cite “Hubbard et al (2011)”
  23. 23. Embedded object collections // Exhibit A: citation @interface MPCitation : MPManagedObject /** Array of MPCitationItem objects. */ @property (readwrite) NSArray *embeddedCitationItems; @end @interface MPCitationItem : MPEmbeddedObject @property (readonly, strong) MPBibliographyEntry *bibliographyEntry; @end
  24. 24. Querying data /** Query the view with the given keys, * and return managed object representations. * * @param view name of the view queried. * @param an array of keys, or nil * @return * */ - (NSArray *)objectsMatchingQueriedView:(NSString *)view keys:(NSArray *)keys; - (NSArray *)contributorsInRole:(NSString *)role { return [self objectsMatchingQueriedView:@"contributorsByRole" keys:@[role]]; }
  25. 25. Respond to object graph changes • Model objects and repositories observe for add / update / delete. • A callback naming convention for notification handlers. • Automated cache busting. - (NSArray *)borderStyles { if (!_cachedBorderStyles) { _cachedBorderStyles = [self stylesOfClass:MPBorderStyle.class]; } return _cachedBorderStyles; }
  26. 26. Cascading object look-ups 1. Get data from a database package. 2. If you found it, get it. 3. If you didn't find it, fall back to shared database package. • Use-case: cascading style system. • A more complex case: effective properties (follow a tree). • Visitors: deep save a subgraph of objects.
  27. 27. Built-in support for tracking contributorship "Get authors contributing to paragraph X?" "Get content contributed to by Y" "Get me the author who most recently contributed to figure Z?"
  28. 28. Pasteboard support • NSPasteboard writing and reading support. • An 'object reference': a fully qualified identifier for object. • Uniquely identifies object also when multiple DBs with same document revision are open. • Handy for supporting drag & drop.
  29. 29. HTTP listener • Easy API for spinning up the HTTP listener. • Try a port, back off, then try again a few more times before failing. /** If callback is not implemented, listener is added. * If callback is implemented, YES return value causes a listener to be created, * NO leads to it being omitted. * * Listener is by default not started for XPC services, * command line tools and when the package controller * has not been set to synchronize peerlessly (`-synchronizesPeerlessly`). */ - (BOOL)packageControllerRequiresListener:(MPDatabasePackageController *)packageController;
  30. 30. State initialisation & placeholder 1. Bundle data by loading a JSON file (MPManuscriptTemplatesController --> manuscript-templates.json) 2. Bundle data by pulling from a bundled CBL database (MPManuscriptTemplatesController --> manuscript-templates.cblite). 3. Bundle data by copying a bundled CBL database in. • Initial state (e.g. required documents at expected state). • Placeholder content: e.g. on-boarding content. - (id)ensureInitialStateInitialized { return [self ensurePrimaryManuscriptInitialized]; }
  31. 31. Saving • 'session' property: track which party created a certain revision. • 'contributor': documents have an array of ordered contributor IDs. • e.g. distinguish changes between different devices / writers.
  32. 32. Model mixins + (void)implementProtocol:(Protocol *)protocol andProtocolsMatching:(MPAdoptedProtocolPatternBlock)patternBlock overloadMethods:(BOOL)overloadMethods; + (void)initialize { if (self == [MPDraft class]) [self implementProtocol:@protocol(MPDeliverable) overloadMethods:NO]; }
  33. 33. @protocol MPDeliverable <NSObject> @optional // methods optional to avoid compiler errors with Objective-Mixin @property (readwrite, strong) NSDate *deadline; @property (readwrite, strong) MPContributor *responsibleContributor; @property (readwrite) MPImportance importance; @property (readonly) NSString *humanReadableImportance; @property (readwrite, strong) MPLabel *statusLabel; @property (readwrite) MPProgressLevel progressLevel; @property (readonly) NSString *humanReadableProgressLevel; @property (readonly, strong) NSArray *requirements; @property (readonly) CGFloat progress; - (void)addRequirement:(MPRequirement *)requirement; - (BOOL)removeRequirement:(MPRequirement *)requirement; - (MPRequirementEvaluation)meetsRequirement:(MPRequirement *)req; @end
  34. 34. Stapler: a utility for bundling data from JSON to a CBL 'database package' Input: • A series of JSON files describing documents to load. • Optional: path to attachment metadata. Output: a Feather database package. Stapler --target ./csl/bundles --objectType MPBundle --source ./csl/csl-data.json --attachments ./csl/csl-paths.json
  35. 35. feather-grep: a Node.js commandline helper for grepping Feather data. ➜ Projects feather-grep -o MPManuscriptCategory -k name,desc,objectType '/Users/mz2/Library/Application Support/Manuscripts' 'MPManuscriptCategory:F9EC50FB-7E8D-4D47-BBEC-7A4E709B9306' : '1-1db4dade18b4d106d779cb640113c0bb' { name: 'Book', desc: 'Books & Book chapters', objectType: 'MPManuscriptCategory' } 'MPManuscriptCategory:714D3A21-C0F9-46A9-80D8-13796818098B' : '1-985e08c3be9829986719241f03eda379' { name: 'Dissertation', desc: 'Master's & PhD theses, and other dissertations.', objectType: 'MPManuscriptCategory' }
  36. 36. Model scriptability (JS / AppleScript) • A scripting category for model and repository classes. • A 1000+ line .sdef XML with mostly silent error handling --> nightmare. • Hence wrote MPScriptingDefinitionManager: a runtime .sdef validator.
  37. 37. Thank you! (@mz2)