android Room이 2.7.0-aplha01 버전 부터 정식으로 Kotlin MultiPlatform을 지원합니다.
자세한 정보는 여기에서 버전정보를 확인 할 수 있습니다.
현재는 24년 6월 12일 자로 2.7.0-aplha04 버전이 최신버전입니다.
CMP Wizard를 이용하여 프로젝트를 세팅하겠습니다.
제가 해당 프로젝트에 기여하여 Room선택을 할수 있도록 만들어 두었습니다.^^v
SQlDelight > OTHER > Room 선택하면 기본으로 종속성을 들고올수 있습니다.(추가로 ksp 세팅까지)
공식 문서를 기반으로 TODO DataBase를 만들어 보겠습니다.
이제 코드를 작성해 봅시다
가장 기본이 되는 곳이죠
사실 기본적인 뼈대는 비슷합니다.
먼저 entity 생성을 해보겠습니다.
//commonMain/~~/database/entity/TodoEntity.kt
@Entity
data class TodoEntity(
@PrimaryKey(autoGenerate = true) val id: Long = 0,
val title: String,
val content: String
)
//commonMain/~~/database/dao/TodoDao.kt
@Dao
interface TodoDao {
@Insert
suspend fun insert(item: TodoEntity)
@Query("SELECT count(*) FROM TodoEntity")
suspend fun count(): Int
@Query("SELECT * FROM TodoEntity")
fun getAllAsFlow(): Flow<List<TodoEntity>>
}
@Database(entities = [TodoEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun getDao(): TodoDao
}
internal const val DB_NAME = "MtDatabaase.db"
expect fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase>
fun getRoomDatabase(): AppDatabase {
return getDatabaseBuilder()
// .addMigrations(MIGRATION_1_2)
// .fallbackToDestructiveMigrationOnDowngrade()
// .setDriver(BundledSQLiteDriver())
.setQueryCoroutineContext(Dispatchers.IO)
.build()
}
context에 종속되는 사항이 있어서 Koin쓰시는게 좋습니다
actual fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
val appContext : Context = KoinJavaComponent.getKoin().get()
val dbFile = appContext.getDatabasePath(DB_NAME)
return Room.databaseBuilder<AppDatabase>(
context = appContext,
name = dbFile.absolutePath
)
}
공식문서에서는 바로 instantiateImpl를 활용하라 되어있으나
ksp 추가시
dependencies {
with(libs.room.compiler) {
add("kspAndroid", this)
add("kspIosX64", this)
add("kspIosArm64", this)
add("kspIosSimulatorArm64", this)
}
}
와 같이 iosMain에 대해서 만들어지지 않습니다.(kspIos,KspIosMain)이런거는 안되더라구요;; 다른방법 아시면 공유점
그래서 iosMain에는 공통 path 제공자만 만들어 두겠습니다.
@OptIn(ExperimentalForeignApi::class)
internal fun providePath() :String {
val directory: NSURL? = NSFileManager.defaultManager.URLForDirectory(
directory = NSLibraryDirectory,
inDomain = NSUserDomainMask,
appropriateForURL = null,
create = false,
error = null,
)
return (requireNotNull(directory).path + "/$DB_NAME")
}
공식 문서에는 NSHomeDirectory 쓰라고 되어있지만 접근이 안되는 곳입니다.
NSLibraryDirectory에 추가해줍시다!
그리고 각각 iosArm64/iosX64/iosSimulatorArm64에 대하여
actual fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
return Room.databaseBuilder<AppDatabase>(
name = providePath(),
factory = { AppDatabase::class.instantiateImpl() }
)
}
을 추가해 줍니다.
간단하네요 끝
ksp에서 만들어지는 dao에서
error: Unresolved reference 'recursiveFetchLongSparseArray'.
FetchLongSparseArray 정보를 못가져 오는 경우가있다.
@Relation 관계로 만든 놈들이 문제가있는거 같은데
실제 코드를 보면 recursiveFetchMap만 연결이 되어있다.
실제로 필요한 코드는
요녀석들인데 아직 지원을 안해주는거 같다. 그래서 임시 방편으로 iosMain에
package androidx.room.util
import androidx.annotation.RestrictTo
import androidx.collection.LongSparseArray
/**
* Same as [recursiveFetchHashMap] but for [LongSparseArray].
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
fun <V> recursiveFetchLongSparseArray(
map: LongSparseArray<V>,
isRelationCollection: Boolean,
fetchBlock: (LongSparseArray<V>) -> Unit
) {
val tmpMap = LongSparseArray<V>(999)
var count = 0
var mapIndex = 0
val limit = map.size()
while (mapIndex < limit) {
if (isRelationCollection) {
tmpMap.put(map.keyAt(mapIndex), map.valueAt(mapIndex))
} else {
// Safe because `V` is a nullable type arg when isRelationCollection == false
@Suppress("UNCHECKED_CAST")
tmpMap.put(map.keyAt(mapIndex), null as V)
}
mapIndex++
count++
if (count == 999) {
fetchBlock(tmpMap)
if (!isRelationCollection) {
map.putAll(tmpMap)
}
tmpMap.clear()
count = 0
}
}
if (count > 0) {
fetchBlock(tmpMap)
if (!isRelationCollection) {
map.putAll(tmpMap)
}
}
}
를 직접 넣어주면 잘 작동한다. 수정될떄까지는 이리쓰자