Skip to content

feat: Persist tasks across sessions using Room database#2

Draft
jsb-nci with Copilot wants to merge 2 commits into
mainfrom
copilot/featureroom-database
Draft

feat: Persist tasks across sessions using Room database#2
jsb-nci with Copilot wants to merge 2 commits into
mainfrom
copilot/featureroom-database

Conversation

Copilot AI commented Mar 9, 2026

Copy link
Copy Markdown

Tasks were lost on app restart due to in-memory mutableListOf<Task>() storage. Replaces TaskRepositoryImpl with a Room-backed implementation and wires async data flow through the ViewModel via coroutines.

Gradle

  • Added room = "2.7.1" and ksp = "2.0.21-1.0.28" to libs.versions.toml with corresponding library aliases and KSP plugin
  • Applied alias(libs.plugins.ksp) in app/build.gradle.kts; added room-runtime, room-ktx, and ksp(room-compiler) dependencies

Room Layer (data/local/)

  • TaskEntity@Entity(tableName = "tasks") mirroring the Task domain model
  • TaskDao — reactive getAllTasks(): Flow<List<TaskEntity>> plus one-shot suspend queries for insert/update/delete and getTaskById
  • TaskDatabase — thread-safe singleton via double-checked locking

Repository

  • TaskRepository interface updated: getTasks() now returns Flow<List<Task>>; mutation methods are suspend
  • New RoomTaskRepositoryImpl(dao: TaskDao) replaces in-memory impl; includes TaskEntity.toTask() / Task.toEntity() extension functions
  • TaskRepositoryImpl retained as-is (no longer implements the interface)

ViewModel & Wiring

  • HomeViewModel now accepts TaskRepository as a constructor parameter and collects the Flow in init via viewModelScope:
    init {
        viewModelScope.launch {
            repository.getTasks().collect { tasks ->
                _uiState.update { it.copy(task = tasks) }
            }
        }
    }
  • HomeViewModelFactory provides the repository to the ViewModel without Hilt
  • MainActivity instantiates TaskDatabase → RoomTaskRepositoryImpl → HomeViewModelFactory and passes the factory to HomeScreen
  • HomeScreen accepts an optional viewModelFactory: ViewModelProvider.Factory? parameter
Original prompt

Goal

Replace the current in-memory TaskRepositoryImpl with a Room database implementation so tasks persist across app sessions. All changes should go on a new branch called feature/room-database.


Project Context

  • Package: com.example.composebasics
  • Language: Kotlin
  • UI: Jetpack Compose + Material 3
  • Architecture: MVVM + Repository Pattern
  • Current issue: TaskRepositoryImpl uses an in-memory mutableListOf<Task>() — data is lost when app is closed

Existing files to be aware of:

domain/model/Task.kt

data class Task(
    val id: String,
    val title: String,
    val isCompleted: Boolean = false
)

data/repository/TaskRepository.kt (interface)

interface TaskRepository {
    fun getTasks(): List<Task>
    fun addTask(title: String): List<Task>
    fun toggleTask(taskId: String): List<Task>
    fun deleteTask(taskId: String): List<Task>
}

data/repository/TaskRepositoryImpl.kt (current in-memory impl)

class TaskRepositoryImpl: TaskRepository {
    private val tasks = mutableListOf<Task>()
    // ... in-memory operations
}

ui/home/HomeViewModel.kt (currently sync, no coroutines)

class HomeViewModel: ViewModel() {
    private val repository: TaskRepository = TaskRepositoryImpl()
    private val _uiState = MutableStateFlow(HomeUIState(task = repository.getTasks()))
    val uiState: StateFlow<HomeUIState> = _uiState

    fun addTask(title: String) { ... }
    fun toggleTask(taskId: String) { ... }
    fun deleteTask(taskId: String) { ... }
    fun setFilter(filter: TaskFilter) { ... }
    fun getVisibleTasks(state: HomeUIState): List<Task> { ... }
}

gradle/libs.versions.toml (current — no Room versions)

[versions]
agp = "8.13.2"
kotlin = "2.0.21"
coreKtx = "1.10.1"
...
composeBom = "2024.09.00"

[libraries]
# ... no Room libraries yet

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }

app/build.gradle.kts (current — no Room, no KSP)

plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
    alias(libs.plugins.kotlin.compose)
}
android {
    namespace = "com.example.composebasics"
    compileSdk { version = release(36) }
    defaultConfig {
        applicationId = "com.example.composebasics"
        minSdk = 29
        targetSdk = 36
        ...
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
    kotlinOptions { jvmTarget = "11" }
    buildFeatures { compose = true }
}
dependencies {
    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.lifecycle.runtime.ktx)
    implementation(libs.androidx.activity.compose)
    implementation(platform(libs.androidx.compose.bom))
    // ... compose deps
    implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.10.0")
}

What needs to be done

1. Update gradle/libs.versions.toml

Add Room version and KSP plugin version:

[versions]
# add:
room = "2.7.1"
ksp = "2.0.21-1.0.28"

[libraries]
# add:
androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }

[plugins]
# add:
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }

2. Update app/build.gradle.kts

  • Add alias(libs.plugins.ksp) to plugins block
  • Add Room dependencies:
implementation(libs.androidx.room.runtime)
implementation(libs.androidx.room.ktx)
ksp(libs.androidx.room.compiler)

3. Create app/src/main/java/com/example/composebasics/data/local/TaskEntity.kt

@Entity(tableName = "tasks")
data class TaskEntity(
    @PrimaryKey val id: String,
    val title: String,
    val isCompleted: Boolean = false
)

4. Create app/src/main/java/com/example/composebasics/data/local/TaskDao.kt

@Dao
interface TaskDao {
    @Query("SELECT * FROM tasks ORDER BY rowid ASC")
    fun getAllTasks(): Flow<List<TaskEntity>>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertTask(task: TaskEntity)

    @Update
    suspend fun updateTask(task: TaskEntity)

    @Query("DELETE FROM tasks WHERE id = :taskId")
    suspend fun deleteTask(taskId: String)
}

5. Create app/src/main/java/com/example/composebasics/data/local/TaskDatabase.kt

@Database(entities = [TaskEntity::class], version = 1, exportSchema = false)
abstract class TaskDatabase : RoomDatabase() {
    abstract fun taskDao(): TaskDao

    companion object {
        @Volatile
        private var INSTANC...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

*This pull request was created from Copilot chat.*
>

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

…ence

Co-authored-by: jsb-nci <212698004+jsb-nci@users.noreply.github.com>
Copilot AI changed the title [WIP] Replace in-memory TaskRepositoryImpl with Room database implementation feat: Persist tasks across sessions using Room database Mar 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants