From the 103 session in the Couchbase LIVE New York mobile track, we went over the feature within Couchbase Lite that would allow us to enable peer-to-peer syncing between two or more devices. From the “Couchbase Mobile 103: Building a Peer-to-Peer App with Couchbase Mobile” slides, we explored in depth the code and components that allowed the photo-sharing application to exchange pictures with another client device running Couchbase Lite through the P2P feature.
The P2P PhotoDrop sample application can be found on the GitHub repo for iOS and Android. In this blog, we will recap at a high level the Couchbase Lite features and APIs that were presented at the Couchbase 103 session as well as some of the code found in the Photo Sharing sample. You may reference the Couchbase Mobile 101 and 102 blogs for guidance on getting started.
Here are the story boards of the application where we have three simple ViewControllers where the ViewController is the entry point to the application. The UX flow begins where the sender will be selecting the photos from the phone and then clicking send. The camera will then open to scan the other phone’s receiving QR code in order to authenticate and initiate the transfer. Let us walk through the core components of the code for the application that enables peer-to-peer feature from Couchbase Lite.
[1] Start Listener
In the ReceiveViewController, we set the port to be equal to zero so that we will let Couchbase Lite decide what to use. This means that everytime we want to share the photos, the port will be changing everytime it initiates a transfer. We enable the authentication where the authentication is the digest and supported by Couchbase Lite listener by default. After that we have the username and password and we set the username/password for authentication. Upon which, we are ready to start the listeners.
iOS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
listener = CBLListener(manager: CBLManager.sharedInstance(), port: 0) listener.requiresAuth = true let username = secureGenerateKey(NSCharacterSet.URLUserAllowedCharacterSet()) let password = secureGenerateKey(NSCharacterSet.URLPasswordAllowedCharacterSet()) listener.setPasswords([username : password]) var success = listener.start(&error) if (success) { syncUrl = genearteSyncURL(listener.URL, username: username, password: password, dbName: database.name) startObserveDatabaseChange() } |
Once the listener is started, we generate the sync URL of the URL endpoint to the receiver database by using the listener URL with the username/password and with the database name. We set the URL into the sync URL variable and we use it later when we generate the QR code from which we then start the database change listeners accordingly.
[2] Observe Database Changes
We start the database change listener so that we know when the photo gets sync to the receiver database. When that happens we get the shared photos from the shared documents and accepts them into the device.
iOS
1 2 3 4 5 6 7 8 9 10 11 12 |
func startObserveDatabaseChange() { NSNotificationCenter.defaultCenter().addObserverForName( kCBLDatabaseChangeNotification, object: database, queue: nil) { (notification) -> Void in if let changes = notification.userInfo!["changes"] as? [CBLDatabaseChange] { for change in changes { dispatch_async(dispatch_get_main_queue(), { self.saveImageFromDocument(change.documentID) }) } } } } |
[3] Generate and Display QR Code
For the last step of the ReceiveViewController where once we have the Couchbase Lite listeners, we then setup the database shared listeners and then we have the Sync URL. With the Sync URL, we are able to generate the QR code. In order to do that we use the iOS core image filter. The QR code contains the listener port and bundled username/password for authentication.
[4] Scan QR Code
Now within the SendViewController, we use the iOS class AVCaptureSession, which is part of the AV Foundation framework with the output type AVMetadataObjectTypeQRCode. This will now allow us to step up the QR code scanner and scan the QR code.
[5] Create Documents
Now we iterate on the photos that we want to send to the receivers. For each photo, we create a document and then we attach the photo to the document in order to then save to the database. The last snippet, we will save the document IDs into the docIds variable which is a property that will be used in the following step.
iOS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
var docIds: [String] = [] for asset in sharedAssets! { //... var data = NSData(bytesNoCopy: imageBytes, length: buffered, freeWhenDone: true) let doc = database.createDocument() let rev = doc.newRevision() rev.setAttachmentNamed("photo", withContentType: "application/octet-stream", content: data) let saved = rev.save(&error) if saved != nil { docIds.append(doc.documentID) } } |
[6] Replicate Documents
The last step now that we have the sync URL that we got from the QR code is to replicate the documents accordingly. We have the photo documents from the database and we are ready to replicate from the sender client device to the receiver device. Here we create a push replication with the sync URL where we send the document ID to the replicator so letting the replicator push for the documents of the ID. And as an optional step, we set the states for the replication change listeners to capture the current status.
iOS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
replicator = database.createPushReplication(url) replicator.documentIDs = docIds NSNotificationCenter.defaultCenter().addObserverForName(kCBLReplicationChangeNotification, object: replicator, queue: nil) { (notification) -> Void in if self.replicator.lastError == nil { var totalCount = self.replicator.changesCount var completedCount = self.replicator.completedChangesCount if completedCount > 0 && completedCount == totalCount { self.statusLabel.text = "Sending Completed” } } else { self.statusLabel.text = "Sending Abort" } } replicator.start() |
Summary
The key implementation points for the Photodrop applications are that it uses the QR code for peer discovery where we generate a one time user name and password for authentication. The core code for sending/receiving photos is around 100 lines of code and contains no code that is directly involved in network communication.