avatarNiranjanky

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

5016

Abstract

  • means all</p><blockquote id="a4c9"><p>Now let’s add an action that interts the new task into our table.</p></blockquote><div id="54bd"><pre>insertTask: <span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">OR</span> IGNORE <span class="hljs-keyword">INTO</span> TaskDb(id, title) <span class="hljs-keyword">VALUES</span> (?,?);</pre></div><p id="5bb1"><b>IGNORE</b> will ignore all the potential errors for Eg. duplications of your title.</p><p id="7064"><b>(?,?)</b> are placeholders that will subsitute with your real values.</p><blockquote id="d9b9"><p>Now let’s add a actions that finishes tht addition of our new task</p></blockquote><div id="89d3"><pre>updateIsCompleted: <span class="hljs-keyword">UPDATE</span> TaskDb <span class="hljs-keyword">SET</span> isCompleted <span class="hljs-operator">=</span> ? <span class="hljs-keyword">WHERE</span> id <span class="hljs-operator">=</span> ?;</pre></div><p id="3f8a">go to build.gradle.kts > Apply the SQLDelight Gradle plugin in your project</p><div id="e3e9"><pre>buildscript { dependencies { <span class="hljs-built_in">classpath</span>("com.squareup.sqldelight:gradle-plugin:<span class="hljs-number">1.5</span>.<span class="hljs-number">5</span>") } }</pre></div><div id="fe96"><pre><span class="hljs-keyword">const</span> <span class="hljs-keyword">val</span> sqlDelight = <span class="hljs-string">"com.squareup.sqldelight"</span>

plugins { id(sqlDelight) }</pre></div><blockquote id="1fd0"><p>add this block to make the <b>SQLDelight</b> <b>Gradle</b> plugin to read the <b>Table.sq</b> file.</p></blockquote><div id="ee6d"><pre>sqldelight { <span class="hljs-title function_">database</span>(<span class="hljs-params"><span class="hljs-string">"TaskerDb"</span></span>) { packageName = <span class="hljs-string">"com.niranjan.khatri.tasker"</span> schemaOutputDirectory = <span class="hljs-title function_">file</span>(<span class="hljs-string">"src/commonMain/sqldelight/com/niranjan/khatri/tasker/db"</span>) } }</pre></div><blockquote id="c392"><p>add the dependencies for <b>drivers</b> on all platforms. <b>Drivers</b> are required to run your statements.</p></blockquote><div id="b321"><pre><span class="hljs-comment">// Database</span> <span class="hljs-keyword">object</span> Database { <span class="hljs-keyword">const</span> <span class="hljs-keyword">val</span> sqlDelightAndroid = <span class="hljs-string">"com.squareup.sqldelight:android-driver:<span class="hljs-subst">{Versions.sqlDelightVersion}</span>"</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">val</span> sqlDelightiOS = <span class="hljs-string">"com.squareup.sqldelight:native-driver:<span class="hljs-subst">{Versions.sqlDelightVersion}</span>"</span> }</pre></div><p id="2b92"><b>Android</b></p><div id="97a2"><pre>val androidMain by getting { dependencies { <span class="hljs-built_in">with</span>(Deps){ <span class="hljs-built_in">implementation</span>(Deps.Database.sqlDelightAndroid) } } }</pre></div><p id="47b2"><b>iOS</b></p><div id="3a69"><pre><span class="hljs-keyword">val</span> iosMain <span class="hljs-keyword">by</span> getting { dependsOn(commonMain) dependencies { implementation(Deps.Database.sqlDelightiOS) } iosX64Main.dependsOn(<span class="hljs-keyword">this</span>) iosArm64Main.dependsOn(<span class="hljs-keyword">this</span>) iosSimulatorArm64Main.dependsOn(<span class="hljs-keyword">this</span>) }</pre></div><blockquote id="7a3f"><p>Sync your Gradle</p></blockquote><h1 id="d224">How to use the database in your app ? Follow the steps:</h1><p id="e044">Setup a Database Helper : It’s a common interface that abstracts the database you’re using.</p><blockquote id="5e5e"><p>go to <b>commonMain</b> > <b>data</b> > create a file <b>DatabaseHelper.kt</b></p></blockquote><div id="48f5"><pre><span class="hljs-keyword">class</span> <span class="hljs-title class_">DatabaseHelper</span>( sqlDriver: SqlDriver <span class="hljs-comment">// inject using KOIN for each platform</span> ) { <span class="hljs-comment">/** * We need Driver to run SQL statements */</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> dbRef : TaskerDb = TaskerDb(sqlDriver)

<span class="hljs-comment">// Fetch all Task from database </span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">fetchAllItems</span><span class="hljs-params">()</span></span>: List&lt;TaskDb&gt; =
    dbRef.tableQueries.selectAll().executeAsList()

<span class="hljs-comment">// insert new Task </span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">insertTask</span><span class="hljs-params">(id: <span class="hljs-type">String</span>, title: <span class="hljs-type">String</span>)</span></span> {
    dbRef.tableQueries.insertTask(id, title)
}

Options

<span class="hljs-comment">// to update the task added is complete </span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">updateIsCompleted</span><span class="hljs-params">(id: <span class="hljs-type">String</span>, isCompleted: <span class="hljs-type">Boolean</span>)</span></span>{
    dbRef.tableQueries.updateIsCompleted(
        isCompleted.toLong(), id
    )
}

<span class="hljs-function"><span class="hljs-keyword">fun</span> TaskDb.<span class="hljs-title">isCompleted</span><span class="hljs-params">()</span></span>: <span class="hljs-built_in">Boolean</span> = <span class="hljs-keyword">this</span>.isCompleted != <span class="hljs-number">0L</span>

<span class="hljs-keyword">internal</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-built_in">Boolean</span>.<span class="hljs-title">toLong</span><span class="hljs-params">()</span></span>: <span class="hljs-built_in">Long</span> = <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>) <span class="hljs-number">1L</span> <span class="hljs-keyword">else</span> <span class="hljs-number">0L</span>

}</pre></div><blockquote id="7cd5"><p>Go to the <b>Repository</b> and inject the <b>DatabaseHelper</b></p></blockquote><div id="cca8"><pre><span class="hljs-keyword">class</span> <span class="hljs-title class_">TaskerRespository</span>( <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> databaseHelper: DatabaseHelper ) {

<span class="hljs-keyword">val</span> tasks: List&lt;TaskUiModel&gt;
    <span class="hljs-keyword">get</span>() = databaseHelper.fetchAllItems().map { TaskUiModel(
        id = it.id,
        title = it.title,
        isCompleted = it.isCompleted != <span class="hljs-number">0L</span>,
        type = it.type.getType()
    )}

<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">createTasks</span><span class="hljs-params">(title: <span class="hljs-type">String</span>, taskType: <span class="hljs-type">TaskType</span>)</span></span> {
    databaseHelper.insertTask(
        id = UUID().toString(),
        title = title,
        type = taskType.value,
        isCompleted = <span class="hljs-literal">false</span>,
    )
}

<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">markTasks</span><span class="hljs-params">(id: <span class="hljs-type">String</span>, isCompleted: <span class="hljs-type">Boolean</span>)</span></span> {
    databaseHelper.updateIsCompleted(id, isCompleted)
}

}</pre></div><h2 id="0f79">Now inject the dependencies in Koin for each platform</h2><blockquote id="2730"><p><b>commonMain</b> > <b>KoinCommon.kt</b></p></blockquote><div id="1455"><pre>object Modules{ val core = module { <span class="hljs-keyword">factory</span> { Platform() } <span class="hljs-keyword">factory</span> { DatabaseHelper(<span class="hljs-keyword">get</span>()) } }

val repositories = module {
    <span class="hljs-keyword">factory</span> { TaskerRespository(<span class="hljs-keyword">get</span>()) }
}

val viewModels = module {
    <span class="hljs-keyword">factory</span> { TaskViewModel(<span class="hljs-keyword">get</span>()) }
    <span class="hljs-keyword">factory</span> { AboutViewModel(<span class="hljs-keyword">get</span>(), <span class="hljs-keyword">get</span>()) }
}

}</pre></div><h2 id="5c5c">Android</h2><blockquote id="eb93"><p><b>androidMain</b> > <b>KoinAndroid.kt</b></p></blockquote><div id="3cad"><pre><span class="hljs-keyword">actual</span> <span class="hljs-keyword">val</span> platformModule = module { single<Settings> { AndroidSettings(<span class="hljs-keyword">get</span>()) } single<SqlDriver> { AndroidSqliteDriver(TaskerDb.Schema, <span class="hljs-keyword">get</span>(), <span class="hljs-string">"TaskerDb"</span>) } }</pre></div><h2 id="ede5">iOS</h2><blockquote id="b4b1"><p><b>iOSmain</b> > <b>KoinIOS.kt</b></p></blockquote><div id="35d0"><pre>actual <span class="hljs-type">val</span> <span class="hljs-variable">platformModule</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">module</span> { single<SqlDriver> { NativeSqliteDriver(TaskerDb.Schema, <span class="hljs-string">"TaskerDb"</span>) } }</pre></div><blockquote id="e751"><p>Build and Run your App</p></blockquote><figure id="6258"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*8_9h-DqW_J404j9CxdaRxg.png"><figcaption></figcaption></figure><figure id="551f"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*afEjrmQBNflFv5cpnW0Q4w.png"><figcaption>Android & iOS working version of Task App</figcaption></figure><p id="b49a">Thanks;) Write me if you have any questions and doubts…</p></article></body>

Understanding : SQLDelight a Kotlin Mutiplatform Mobile library for managing database.

Databases are used to persistently store and manage data, such as user information, app settings, and content. Both Android and iOS offer different options for working with databases. Here, I’ll provide an overview of how to store data in a database for both platforms:

Android

provides several options for data storage, but the most commonly used methods are SQLite and Room. Here’s how you can use these options:

iOS

offers several options for data storage as well, with Core Data and SQLite being the most commonly used methods:

Here are some common steps for storing data in a database on both platforms:

Let’s learn about persisting data in KMP:

If you want to store just few bits of information you can use Key-Value Storage mechanism.

For storing larger bits of information, use Database mechanism.

We will discuss Database mechanism here.

Database is a structured collection of datas which we access it using queries. SQLite is the most popular relational database model among the mobile developers.

For Kotlin Multiplatfrom we have a great library called SQLDelight, which generates typesafe Kotlin APIs from your SQL statements and is easy to setup.

Before seting up the SQLDelight lets take the notes:

SQL is a query language used for database it’s not database itself.

Some famouse database used are SQLite, MySQL and PostgresSQL but do not support multiplatform.

Relational database representes data in the structure of the Tables.

Setting up SQLDelight:

go to shared module > commonMain > create a nested directory called db > add this file Table.sq (.sq is the file extension for SQLDelight)

Android Studio will suggest you to download the plugin for sqldelight.

create a table called TaskerDb by writing this block of code in Table.sq file

CREATE TABLE TaskDb (
id TEXT NOT NULL PRIMARY KEY,
title TEXT NOT NULL UNIQUE,
isCompleted INTEGER NOT NULL DEFAULT 0,
dueDate INTEGER
);

Here in this code, we create columns : id, title and isCompleted (in simple terms you can think this as your data class with this fields or properties)

Also you can see we specify the type using TEXT or INTEGER.

NOT NULL is to specify non-nullability.

DEFAULT will let you provide a defualt value for this property.

UNIQUE key will prevent you from duplication.

Note* that Boolean type is not supported so you can represent that using INTEGER type.

Now let’s define the actions you want to perfom in your database

selectAll:
SELECT * FROM TaskDb;

This code will select all items in the TaskerDb table. * means all

Now let’s add an action that interts the new task into our table.

insertTask:
INSERT OR IGNORE INTO TaskDb(id, title)
VALUES (?,?);

IGNORE will ignore all the potential errors for Eg. duplications of your title.

(?,?) are placeholders that will subsitute with your real values.

Now let’s add a actions that finishes tht addition of our new task

updateIsCompleted:
UPDATE TaskDb SET isCompleted = ? WHERE id = ?;

go to build.gradle.kts > Apply the SQLDelight Gradle plugin in your project

buildscript {
    dependencies {
        classpath("com.squareup.sqldelight:gradle-plugin:1.5.5")
    }
}
const val sqlDelight = "com.squareup.sqldelight"

plugins {
    id(sqlDelight)
}

add this block to make the SQLDelight Gradle plugin to read the Table.sq file.

sqldelight {
    database("TaskerDb") {
        packageName = "com.niranjan.khatri.tasker"
        schemaOutputDirectory = file("src/commonMain/sqldelight/com/niranjan/khatri/tasker/db")
    }
}

add the dependencies for drivers on all platforms. Drivers are required to run your statements.

// Database
object Database {
    const val sqlDelightAndroid = "com.squareup.sqldelight:android-driver:${Versions.sqlDelightVersion}"
    const val sqlDelightiOS = "com.squareup.sqldelight:native-driver:${Versions.sqlDelightVersion}"
}

Android

val androidMain by getting {
    dependencies {
        with(Deps){
            implementation(Deps.Database.sqlDelightAndroid)
        }
    }
}

iOS

val iosMain by getting {
    dependsOn(commonMain)
    dependencies {
        implementation(Deps.Database.sqlDelightiOS)
    }
    iosX64Main.dependsOn(this)
    iosArm64Main.dependsOn(this)
    iosSimulatorArm64Main.dependsOn(this)
}

Sync your Gradle

How to use the database in your app ? Follow the steps:

Setup a Database Helper : It’s a common interface that abstracts the database you’re using.

go to commonMain > data > create a file DatabaseHelper.kt

class DatabaseHelper(
    sqlDriver: SqlDriver  // inject using KOIN for each platform
) {
    /**
     * We need Driver to run SQL statements
     */
    private val dbRef : TaskerDb = TaskerDb(sqlDriver)

    // Fetch all Task from database 
    fun fetchAllItems(): List<TaskDb> =
        dbRef.tableQueries.selectAll().executeAsList()

    // insert new Task 
    fun insertTask(id: String, title: String) {
        dbRef.tableQueries.insertTask(id, title)
    }

    // to update the task added is complete 
    fun updateIsCompleted(id: String, isCompleted: Boolean){
        dbRef.tableQueries.updateIsCompleted(
            isCompleted.toLong(), id
        )
    }

    fun TaskDb.isCompleted(): Boolean = this.isCompleted != 0L

    internal fun Boolean.toLong(): Long = if (this) 1L else 0L
}

Go to the Repository and inject the DatabaseHelper

class TaskerRespository(
    private val databaseHelper: DatabaseHelper
) {

    val tasks: List<TaskUiModel>
        get() = databaseHelper.fetchAllItems().map { TaskUiModel(
            id = it.id,
            title = it.title,
            isCompleted = it.isCompleted != 0L,
            type = it.type.getType()
        )}

    fun createTasks(title: String, taskType: TaskType) {
        databaseHelper.insertTask(
            id = UUID().toString(),
            title = title,
            type = taskType.value,
            isCompleted = false,
        )
    }

    fun markTasks(id: String, isCompleted: Boolean) {
        databaseHelper.updateIsCompleted(id, isCompleted)
    }
}

Now inject the dependencies in Koin for each platform

commonMain > KoinCommon.kt

object Modules{
    val core = module {
        factory { Platform() }
        factory { DatabaseHelper(get()) }
    }

    val repositories = module {
        factory { TaskerRespository(get()) }
    }

    val viewModels = module {
        factory { TaskViewModel(get()) }
        factory { AboutViewModel(get(), get()) }
    }
}

Android

androidMain > KoinAndroid.kt

actual val platformModule = module {
    single<Settings> {
        AndroidSettings(get())
    }
    single<SqlDriver> {
        AndroidSqliteDriver(TaskerDb.Schema, get(), "TaskerDb")
    }
}

iOS

iOSmain > KoinIOS.kt

actual val platformModule = module {
    single<SqlDriver> {
        NativeSqliteDriver(TaskerDb.Schema, "TaskerDb")
    }
}

Build and Run your App

Android & iOS working version of Task App

Thanks;) Write me if you have any questions and doubts…

Kmm
Kmp
Android
iOS
Sqldelight
Recommended from ReadMedium