TouchDB demo; mobile wiki for iOS
Objective-C JavaScript Shell
Switch branches/tags
Nothing to show
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
Frameworks
Model
Resources
TouchWiki-Mac
TouchWiki.xcodeproj
TouchWiki
server
vendor
.gitignore
.gitmodules
README.md
iPad icon.png
iPad icon@2x.png

README.md

TouchWiki For iOS

This is a fairly large demo of CouchbaseLite. It's an iPad-only viewer and editor app for a wiki that lives in a database. The wiki functionality is pretty basic, but allows you to view and edit pages (in Markdown format) and link between them. It even supports multiple wiki namespaces.

The schema is compatible with the HTML5 (PhoneGap) wiki demo app, so the two apps can interoperate on the same database. There is also a companion app server for use with the Couchbase Sync Gateway that enables access control so that multiple users can have their own private or shared wikis.

How To Build It

  1. Clone/checkout this repository.
  2. Install the submodule (the Markdown parser) by running git submodule update --recursive from within the checked-out repository directory.
  3. Obtain a recent build of the public_api branch of CouchbaseLite for iOS, and copy CouchbaseLite.framework into the Frameworks subdirectory of this repo.
  4. Open TouchWiki.xcodeproj in Xcode.
  5. Build and run.
  6. ...
  7. Profit!

Issues & Limitations

  • Currently the only form of authentication it supports is Persona (aka BrowserID). That is, if the replication fails with a 401 status, the app will respond by popping up a Persona login panel. This should be user-configurable in the sync popup, so it can present a traditional username/password panel instead.
  • The UI shows the owner and member status as part of a wiki page, when they're actually attributes of the wiki. It's not clear that changing the member list of a page actually changes it for all pages in that wiki!

Document Schema

The database contains two types of documents: wikis and pages. Both contain some common properties, as in this example:

"type": "page",
"created_at": "2012-12-09T06:05:55.031Z",
"updated_at": "2012-12-16T01:33:01.909Z"
"markdown": "# Introduction\n\nThis page is _very_ important...",
  • The type property indicates which type of document this is, either "wiki" or "page".
  • created_at is a JSON timestamp of the time the document was created.
  • updated_at is the time the document was last updated by a user.
  • markdown contains rich-text content in Markdown format; either the body of a page, or a description of a wiki. It's optional in a wiki (and actually unused in the iOS app.)

Wiki documents

A wiki document's additional properties look like this:

"_id": "5737529525067657",
"title": "Mobile Dev",
"owner_id": "snej"
"members": ["jchris", "mschoch"],
  • The _id property is a UUID: It's important that it be unique, but it doesn't need to be human-readable.
  • title is the user-visible title as a plain-text (not Markdown) string.
  • owner_id is the user ID of the owner of the wiki.
  • members is an optional array of user IDs of other people who have access to the wiki and its documents.

Page documents

"_id":          "5737529525067657:Sync Client",
"wiki_id":      "5737529525067657",
  • The _id property consists of the owning wiki's _id plus a colon (":") plus the user-visible name of the page.
  • wiki_id is the owning wiki's ID; this is redundant with the _id but is present so that map functions can access it more quickly.

The reason the page name is stored directly in the document ID, instead of in a property, is to make wiki links efficient: when rendering an HTML page, the app can take the name of the destination page from the wiki link and construct the URL without having to do any database queries.

Application Code Overview

Object Model

The application's object model is based on CouchModel.

  • The abstract class WikiItem declares the common properties and contains a bit of code to manage them.
  • Wiki inherits from WikiItem. It manages the wiki-specific properties, and contains methods to create pages and to get a list of all pages or their titles.
  • WikiPage also inherits from WikiItem. It knows how to look up the parent Wiki object by deriving its ID.
  • WikiStore is a singleton; actually there is one per wiki database, but there's only one database. It acts as the parent of all wikis and includes methods for creating and enumerating wikis.

Drafts

WikiPage has an interesting persistent property named draft that isn't in the official schema; it's actually private to this app. This property allows edited documents to be saved to local storage, without making the changes publicly visible yet. There are actually three stages to editing:

  1. User edits the text onscreen. Each keystroke changes the in-memory WikiPage.markdown property, and also sets the draft property to true.
  2. After a delay, or when the app is switched out, the page object autosaves to the local database. But the presence of the draft property causes the replication filter to skip this document revision when syncing.
  3. Finally, when the user presses the Save button the draft property is removed and the page is saved again. Now the replicator will push the change to the server.

This is a pretty useful technique for any application where a database is shared between multiple users, and users want to be able to make local changes without having them immediately visible to others. (A simpler way is to push only on user request, but that causes all local changes to be pushed; using a draft property gives the user more fine-grained control.)

Sync UI

The app includes some classes for managing the user interface for syncing. These are intended to be reuseable, and might eventually end up in CouchbaseLite itself.

SyncManager is a thin wrapper around CBLDatabase's sync API that manages a pair of replications to and from a server. It supports a delegate object that can customize replication objects and will be notified of the progress of replication.

SyncButton is a UI front-end for sync. It's a UIBarButtonItem that can be put in the app's nav bar or toolbar. It displays a "cloud" icon that represents the current state of sync. Right now the appearance is fairly crude, but it does show a progress bar during replication, and highlights the icon if there's an error. Tapping the button opens a pop-up window that lets the user configure sync: there's a text field for the remote database URL, a slider to enable continuous syncing, and a button to trigger a manual sync. It also displays a brief error message if sync failed.

App UI Controllers

The rest of the app code is a hopefully-straightforward set of iOS UI controllers.

AppDelegate is the application delegate; it just initializes CouchbaseLite and constructs the app's top-level UI components.

PageController manages the primary window contents: the display of the current page, the navigation bar, and the split-view that overlays the page list. This is the biggest class in the app since it does several different things; it might be refactored in the future.

WikiListController controls the top-level table view that lists all wikis, using a CBLUITableSource

PageListController controls the second-level table view that lists all pages in the currently selected wiki, also using a CBLUITableSource.

PageEditController runs the editable-text view that's overlaid on the PageController while a page is being edited.