Couchbase Mobile brings the power of NoSQL to the edge. It is comprised of three components:
Couchbase Mobile supports flexible deployment models. You can deploy
This tutorial will walk you through a very basic example of how you can use Couchbase Lite 3 in standalone mode within your Swift app. In this mode, Couchbase Lite will serve as a local, embedded data store within your iOS App and can be a replacement for SQLite or Core Data.
You will learn the fundamentals of
You can learn more about Couchbase Mobile here.
This tutorial assumes familiarity with building Swift apps with Xcode.
Note: If you are using an older version of Xcode, which you need to retain for other development needs, make a copy of your existing version of Xcode and install the latest Xcode version. That way you can have multiple versions of Xcode on your Mac. More information can be found in Apple's Developer Documentation.
We will be working with a very simple User Profile app. It does one thing - Allow a user to log in and create or update their user profile data.
The user profile data is persisted as a Document in the local Couchbase Lite Database. So, when the user logs out and logs back in again, the profile information is loaded from the Database.
Figure 1. The sample user profile application running in a simulator
git clone https://github.com/couchbase-examples/ios-swift-cblite-userprofile-standalone.git
Next, we will download the Couchbase Lite 3.0 XCFramework.
The Couchbase Lite iOS XCFramework is distributed via SPM, CocoaPods, Carthage, or you can download the pre-built framework. See the Getting Started - Install documentation for more information.
In our example, we will be downloading the pre-built version of the XCFramework, using a script. To do this, type the following in a command terminal:
cd /path/to/UserProfileDemo/src
sh install_tutorial.sh
Now, let's verify the installation.
UserProfileDemo.xcodeproj
project file, located at /path/to/UserProfileDemo/src
open UserProfileDemo.xcodeproj
Use Xcode to build and run the project using a simulator.
Verify that you see the login screen.
Figure 2. User Profile Login Screen Image
Couchbase Lite is a JSON Document Store. A Document is a logical collection of named fields and values. The values are any valid JSON types. In addition to the standard JSON types, Couchbase Lite supports Date
and Blob
data types. While it is not required or enforced, it is a recommended practice to include a "type" property that can serve as a namespace for related documents.
The app deals with a single Document with a "type" property of "user". The document ID is of the form "user::demo@example".
Example 1. A user profile document example
{
"type":"user",
"name":"Jane Doe",
"email":"jane.doe@earth.org",
"address":"101 Main Street",
"image":CBLBlob (image/jpg)
}
blob
data type that is associated with the profile image - see: Working with Blob.The "user" Document is encoded to a native struct named UserRecord as shown in Example 2.
Example 2. The encoding of a UserRecord to a native structure
let kUserRecordDocumentType = "user"
typealias ExtendedData = [[String:Any]]
struct UserRecord : CustomStringConvertible{
let type = kUserRecordDocumentType
var name:String?
var email:String?
var address:String?
var imageData:Data?
var extended:ExtendedData?
var description: String {
return "name = \(String(describing: name)),
email = \(String(describing: email)),
address = \(String(describing: address)),
imageData = \(imageData?.debugDescription
?? " ")"
}
}
In this section, we will do a code walk-through of the basic Database operations.
When a user logs in, we create an empty Couchbase Lite Database for the user if one does not exist.
openOrCreateDatabaseForUser()
function.func openOrCreateDatabaseForUser(_
user:String,
password:String,
handler:(_ error:Error?)->Void) {
DatabaseConfiguration
. This is an optional step. In our case, we would like to override the default path of the Database. Every user has their own instance of the Database that is located in a folder corresponding to the user.var options = DatabaseConfiguration()
guard let defaultDBPath = _applicationSupportDirectory else {
fatalError("Could not open Application Support Directory for app!")
return
}
// Create a folder for the logged in user if one does not exist
let userFolderUrl = defaultDBPath.appendingPathComponent(user,
isDirectory: true)
let userFolderPath = userFolderUrl.path
let fileManager = FileManager.default
if !fileManager.fileExists(atPath: userFolderPath) {
try fileManager.createDirectory(atPath: userFolderPath,
withIntermediateDirectories: true,
attributes: nil)
}
// Set the folder path for the CBLite DB
options.directory = userFolderPath
_db = try Database(name: kDBName, config: options)
You can be asynchornously notified of any change (add, delete, update) to the Database by registering a change listener with the Database. In our app, we are not doing anything special with the Database change notification other than logging the change. In a real world app, you would use this notification for instance, to update the UI.
registerForDatabaseChanges()
function.fileprivate func registerForDatabaseChanges() {
ListenerToken
as it is needed for removing the listener.dbChangeListenerToken = db?.addChangeListener({ [weak self](change) in
guard let `self` = self else {
return
}
for docId in change.documentIDs {
if let docString = docId as? String {
let doc = self._db?.document(withID: docString)
if doc == nil {
print("Document was deleted")
}
else {
print("Document was added/updated")
}
}
}
})
When a user logs out, we close the Couchbase Lite Database associated with the user and deregister any database change listeners
closeDatabaseForCurrentUser()
function.func closeDatabaseForCurrentUser() -> Bool {
try db.close()
deregisterForDatabaseChanges()
function.fileprivate func deregisterForDatabaseChanges() {
ListenerToken
associated with the listener.if let dbChangeListenerToken = self.dbChangeListenerToken {
db?.removeChangeListener(withToken: dbChangeListenerToken)
}
Will open/create DB at path Will open/create DB at path /Users/yourusername/Library/Developer/CoreSimulator/Devices/E4E62394-9940-4AF8-92FC-41E3C794B216/data/Containers/Data/Application/65EAD047-B29A-400C-803F-F799BAE99CBA/Library/Application Support/demo@example.com
Once an instance of the Couchbase Lite Database is created/opened for the specific user, we can perform basic Document functions on the Database. In this section, we will walkthrough the code that describes basic Document operations
Once the user logs in, the user is taken to the "Your Profile" screen. A request is made to load The "User Profile" Document for the user. When the user logs in the very first time, there would be no user profile document for the user.
userProfileDocId
definition. This document Id is constructed by prefixing the term "user::" to the email Id of the user.lazy var userProfileDocId: String = {
let userId = dbMgr.currentUserCredentials?.user
return "user::\(userId ?? "")"
}()
fetchRecordForCurrentUser()
function.func fetchRecordForCurrentUser(handler:@escaping(_
records:UserRecord?, _ error:Error?)->Void) {
userProfileDocId
from the database.guard let db = dbMgr.db else {
fatalError("db is not initialized at this point!")
}
var profile = UserRecord.init()
profile.email = self.dbMgr.currentUserCredentials?.user
self.associatedView?.dataStartedLoading()
// fetch document corresponding to the user Id
if let doc = db.document(withID: self.userProfileDocId) {
profile.email = doc.string(forKey: UserRecordDocumentKeys.email.rawValue)
profile.address = doc.string(forKey:UserRecordDocumentKeys.address.rawValue)
profile.name = doc.string(forKey: UserRecordDocumentKeys.name.rawValue)
profile.imageData = doc.blob(forKey:UserRecordDocumentKeys.image.rawValue)?.content
}
The code example above does the following:
email
property of the UserRecord with the email Id of the logged in user. This value is not editable.getBlob
type to fetch the value of a property of type Blob
A The "User Profile" Document is created for the user when the user taps the "Done" button on the "Profile Screen". The function below applies whether you are creating a document or updating an existing version
setRecordForCurrentUser()
method.func setRecordForCurrentUser( _ record:UserRecord?,
handler:@escaping(_ error:Error?)->Void) {
Use appropriate type-setters to set the various properties of the Document
mutableDoc.setString(record?.type, forKey: UserRecordDocumentKeys.type.rawValue)
if let email = record?.email {
mutableDoc.setString(email, forKey: UserRecordDocumentKeys.email.rawValue)
}
if let address = record?.address {
mutableDoc.setString(address, forKey: UserRecordDocumentKeys.address.rawValue)
}
if let name = record?.name {
mutableDoc.setString(name, forKey: UserRecordDocumentKeys.name.rawValue)
}
if let imageData = record?.imageData {
let blob = Blob.init(contentType: "image/jpeg", data: imageData)
mutableDoc.setBlob(blob, forKey: UserRecordDocumentKeys.image.rawValue)
}
Specifically, note the support of the setBlob
type to fetch the value of a property of type Blob
.
Save the document
do {
// This will create a document if it does not exist and overrite it if it exists
// Using default concurrency control policy of "writes always win"
try? db.saveDocument(mutableDoc)
handler(nil)
}
catch {
handler(error)
}
We don't delete a Document in this sample app. However, deletion of a document is pretty straightforward and this is how you would do it.
if let doc = db.document(withID: idOfDocToRemove) {
try db.deleteDocument(doc)
}
Congratulations on completing this tutorial!
This tutorial walked you through a very basic example of how to get up and running with Couchbase Lite as a local-only, standalone embedded data store in your iOS app. If you want to learn more about Couchbase Mobile, check out the following links.