Compose Multiplatform 實戰:CMP實作跨平台資料庫SqlDelight
Compose Multiplatform (簡稱CMP)
在 CMP
專案中
如何實現跨平台的資料庫操作呢?
SqlDelight
提供了一個強大的解決方案
本文將介紹如何在跨平台Android
& iOS
環境中
使用 SqlDelight
進行資料庫操作
目錄
- Compose Multiplatform 實戰:放輕鬆點,初探CMP
- Compose Multiplatform 實戰:初戰,安裝CMP環境吧
- Compose Multiplatform 實戰:續戰,用Wizard創建CMP專案
- Compose Multiplatform 實戰:在Android、iOS模擬器上跑CMP專案
- Compose Multiplatform 實戰:CMP的專案結構理解與編譯配置
- Compose Multiplatform 實戰:CMP中跨平台Android、iOS程式碼的進入點
- Compose Multiplatform 實戰:在CMP的Compose中用Material Design3 Theme
- Compose Multiplatform 實戰:CMP用Compose實作跨平台畫面
- Compose Multiplatform 實戰:使用 expect 和 actual 實現跨平台程式碼
- Compose Multiplatform 實戰:CMP中實作Compose Navigation頁面切換
- Compose Multiplatform 實戰:CMP中透過StateFlow來管理UI狀態
- Compose Multiplatform 實戰:CMP中實作NavigationBar底部欄
- Compose Multiplatform 實戰:CMP中使用koin來依賴注入Dependency Injection
- Compose Multiplatform 實戰:CMP實作跨平台資料庫SqlDelight
- Compose Multiplatform 實戰:CMP中使用ROOM開發跨平台資料庫 & 疑難雜症
在專案中導入 SqlDelight
需在libs.version.toml
文件中添加:
[versions]
sqldelight = "2.0.1"
[libraries]
sqldelight-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
sqldelight-native = { module = "app.cash.sqldelight:native-driver", version.ref = "sqldelight" }
sqldelight-coroutines-extensions = { module = "app.cash.sqldelight:coroutines-extensions", version.ref = "sqldelight" }
/** extensions 跟 runtime 可兩選一 * /
/** 主要差異是在extensions有提供常用的flow、emit相關操作使用,runtime則無 * /
sqldelight-runtime = { module = "app.cash.sqldelight:runtime", version.ref = "sqldelight" }
[plugins]
sqlDelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }
並在 build.gradle.kts
中添加插件和依賴
這次部分需要兩大目標平台的實作
CMP
一樣有提供Kotlin
為底的解決方案
所以可以直接在 androidMain
、commonMain
、iosMain
加入對應的依賴項
androidMain.dependencies {
implementation(libs.sqldelight.android)
}
commonMain.dependencies {
implementation(libs.sqldelight.coroutines.extensions)
}
iosMain.dependencies {
implementation(libs.sqldelight.native)
}
同時,一樣是build.gradle.kts
中
kotlin
下面加入sqlDelight的配置
這段gradle配置
可以理解為在test.your.package.db package下
當編譯過後
會幫你建立一個AppDatabase的可操作class
kotlin {
sqldelight {
databases {
create("AppDatabase") {
packageName.set("test.your.package.db")
}
}
}
}
在 commonMain/sqldelight/database
目錄下創建 .sq
文件:
需要在上述路徑加入sqldelight folder
然後Build專案時才會成功產生可操作的class
而sqlDelight
的.sq
提供的方案是讓你透過sql指令
去定義增刪改查 來產出table
.sq
範例如下:
CREATE TABLE VocabularyEntity (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL
);
insert:
INSERT OR REPLACE INTO VocabularyEntity(id, name)
VALUES(?,?);
getAll:
SELECT * FROM VocabularyEntity;
updateName:
UPDATE VocabularyEntity
SET name = :name
WHERE id IS :id;
delete:
DELETE FROM VocabularyEntity
WHERE id IS :id;
前面說過
可能因為Android、iOS平台的相容性不同
所以table相關操作邏輯可以共通
但是不同目標的DatabaseDriver可能需要各別實作
因此
為不同平台創建 DatabaseDriver
可以這樣寫:
// in ../commonMain
expect class DatabaseDriverFactory {
fun create(): SqlDriver
}
// in ../androidMain
actual class DatabaseDriverFactory(private val context: Context) : KoinComponent {
actual fun create(): SqlDriver {
return AndroidSqliteDriver(AppDatabase.Schema, context, "AppDataBase")
}
}
// in ../iosMain
actual class DatabaseDriverFactory {
actual fun create(): SqlDriver {
return NativeSqliteDriver(AppDatabase.Schema, "AppDataBase")
}
}
關鍵程式碼解說
:
AppDatabase.Schema
:這個Schema是配置完上面的Build.gradle.kts
sync後會幫你產生的。AndroidSqliteDriver
: 在 Android平台的需要輸入context
,所以在autual classDatabaseDriverFactory
建構子放入NativeSqliteDriver
:iOS的SqliteDriver
前面天數有提到
當有跨平台
的內容時
可以做一個platformModule
去實作跨平台需要注入的內容
(忘記的可以回CMP中使用koin來依賴注入看~~)
這邊我們直接上範例:
// in ../commonMain
expect val platformModule: Module
// in ../androidMain
actual val platformModule: Module = module {
single { DatabaseDriverFactory(get<Context>()) }
single { AppDatabase(get<DatabaseDriverFactory>().create()) }
}
// in ../iosMain
actual val platformModule: Module = module {
single { DatabaseDriverFactory() }
single { AppDatabase(get<DatabaseDriverFactory>().create()) }
}
關鍵程式碼解說
:
-
platformModule
:commonMain中用expect告知目標平台需要實作對應variable
而在androidMain
、iosMain
中就需要分別去實作autual的variable -
因此我們在目標平台
android
跟iOS
中 就可以用
前面完成的AppDatabase
與DatabaseDriverFactory
並搭配koin
去依賴注入
以下是獲得 AppDatabase
並進行操作的示例
SqlDelight會幫你轉成物件導向可以操作的方法讓你使用
class LearningDataStore (private val db: AppDatabase) {
private val queries = db.vocabularyEntityQueries
fun insert(id: Long?, name: String) {
queries.insert(id = id, name = name)
}
fun getAll() = queries.getAll().asFlow().mapToList(Dispatchers.IO)
fun update(id: Long, name: String) {
queries.updateName(id = id, name = name)
}
fun delete(id: Long) {
queries.delete(id = id)
}
}
這是前期CMP
提供的一個local db的一個解決方案
用來共用跨平台的db邏輯
不過目前用起來比較沒那麼直觀
因為需自己寫整篇sql指令
但能一次撰寫雙平台邏輯的話
也不失為一種好方法
不過大家不用擔心
後面會再介紹用Room
來實作跨平台local db的一個新方案
是近期2024/05
左右才開始支援的
所以大家到時候也可以來參考看看
下一篇文章
我將會介紹另一種本地資料庫 Room
大家可以參考之後
再決定要用哪一種
Compose Multiplatform 實戰:CMP中使用ROOM開發跨平台資料庫 & 疑難雜症