SQLDelight: Manage Databases in Kotlin Multiplatform (KMP)
Persisting your data in a KMP project can be challenging as implementations vary on different platforms. Fortunately, CashApp has created SQLDelight, a library that allows you to generate a type-safe Kotlin database API from SQL queries. And it works with KMP! Their Getting Started guide is not very comprehensive so I will try to explain in a bit more detail in the context of a KMP project using Compose Multiplatform
SQLDelight implementation in a KMP project
Supposing you have your KMP project set up and running, follow these steps.
Step 1: Set Up SQLDelight Dependencies
Go to your shared module’s build.gradle file and add the following (replace 2.0.0 with the current latest version):
Some important things to note here:
- The generated database Kotlin code will have the name you set up here (MyAppSQLDelightDatabase). I like to include the library name in, so that there is no confusion
- The packageName you set here will be important in step 3.
Also, add the platform-specific dependencies (replace 2.0.0 with the current latest version):
Note that I am also adding the coroutines-extensions dependency to commonMain. It is not necessary but highly likely that you will want to leverage some of its features (like converting queries into Flows).
Step 2: Create platform-specific drivers
Obtaining an SqlDriver is platform-specific, so we need different implementations for each platform.
Add this to the commonMain code:
And then provide androidMain implementation
Note: We are using a static application context here. You can check out this StackOverflow thread for ideas how to get it.
And the iosMain implementation.
Step 3: Define your schema
Now you’re ready to define your database schema. Create one or more .sq files in the sqldelight folder in your commonMain package.
IMPORTANT: the sub-folder needs to match the packageName you’ve set up in step 1! This is where SQLDelight expects the .sq files to be.
Pro tip: Install the SQLDelight Android Studio plugin to make your work easier.
And now you can finally start defining your schema. Typically the first statement in the .sq
file creates a table, but you can also create indexes or set up default content. Example:
From these statements, SQLDelight will generate a Database
class with an associated Schema
object that can be used to create your database and execute statements on it. The Database
class is generated by the generateSqlDelightInterface
Gradle task which is run automatically by the SQLDelight IDE plugin when you edit a .sq
file, and also as part of a normal Gradle build.
If you’ve installed the plugin or build the project now, you should see the generated classes under shared/build/generated/sqldelight.
Step 4: Create a Database instance
I like to create a Database class wrapping the SQLDelight specifics and mapping results to domain objects, Flows etc. All of this is done in the commonMain space. For example like this:
To instantiate the Database class, use a dependency injection framework of your choice. For example with Koin for Compose Multiplatform, the result might look like this:
Module definitions:
In your App Composable:
Step 5: Tweak and test
- In Xcode, you might need to add
-lsqlite3
toOther Liker Flags
in yourBulid Settings
in order to build the project successfully.
- Write tests for your Database.
- Tweak the platform-specific settings if needed.
- Always map the database entities to custom data classes (as shown in step 4), DO NOT use the auto-generated classes directly.
Conclusion
Creating a Database layer in the context of KMP is fairly simple with SQLDelight. There are, however, a few things to watch out for which I tried to highlight in this article. Feel free to provide your feedback and experience in the comments.