SQLite Database Singleton Class for iOS in Swift 3

Posted on
Jun 25, 2017

Writing code for database querying or updating can be tedious, error-prone, and hard to manage. Therefore, abstracting the database operations into a globally accessible model class may be a nice solution. In this article, I will show you how to write a singleton class which wraps the database operations. I will use FMDB (SQLite) with Swift 3 to demonstrate, but it is just about the same for every languages and frameworks.

The Singleton Class

By using the singleton pattern, we can

Make sure only one object of the class is instantiated throughout the app.

Access the object of the class globally.

Since usually, we only have one database across the app, it is a good idea to ensure only one database accessing class is instanced. Not to mention that multiple instances of database accessing class would cause problematic inconsistencies. On top of that, by making the class accessible globally, we can fetch data from the database at ease in any part of the program.

The member sharedInstance is assigned to DatabaseAccessor’s own initializer, so you will have an DatabaseAccessor object available. Furthermore, since sharedInstance is marked as static, it not only will be loaded before any access to DatabaseAccessor but also can be accessed globally by DatabaseAccessor.sharedInstance.

Lastly, the init() is declared as private so it can only be instanced in the scope of the DatabaseAccessor class, implying that only one instance is allowed in the entire program.

Before moving on to abstracting the operations, I have to warn you that though singletons are super convenient, it could cause inconsistency problems if the program is multi-threaded. So, use singletons with caution if your program consists of multiple threads.

Opening and Closing the database

Carry on to implementing the abstraction of database operations. First, let us do the simplest, opening and closing the database (again, I will be using FMDB for simplicity, the syntax may differ for other database frameworks):

In the beginning, we add a private mainDB variable to store the actual database instance. In initializeDatabase(), we store the database path in databasePath, instance a FMDatabase object (the main database instance), and assign it to mainDB.

For closeDatabase(), we simply call mainDB?.close() to close the database.

SELECT, UPDATE, INSERT Operations

Now moving on to the most important part – implementing the database operations. This includes SELECT, UPDATE, and the INSERT operations.

Suppose that we have the table Contact stored in the database, and it consists of the following columns and entries:

Since searchContactByPhone(_:) and searchContactByCity(_:) are similar to searchContactByID(_:) and searchContactByName(_:) respectively, pardon me for omitting them.

We use SELECT * FROM to fetch the entire row and WHERE to limit the search results to certain conditions. Note that for searchContactByID(_:), the return type is Contact?, however, for searchContactByName(_:), the return type is [Contact]. This is because ID is a unique key, the result will be either exactly one or none. If none, we simply return nil. On the other hand, since Name is not a unique key (people may share the same name), we return an array of all matching contacts. If there are no matching results, we will return an empty [Contact].

(Incidentally, because queryResults!.int(forColumn:) returns the type Int32, we should cast it to Int by Int(...).)

Conclusion

By wrapping the operations into a singleton class, accessing database could be made within a single line of code. For example, whenever I need to search for people named Bill, I just need to do a single function call:

P.S.

I am working on the PowerUp-iOS project as a Google Summer of Code (GSoC) student from June to August. Since I am developing everything in Swift 3.0, I will (hopefully) be posting articles about Swift and iOS dev during these couple of months.